Upgrade EJS and replace Moment

This commit is contained in:
Kasra Bigdeli committed 2026-09-06 23:15:16 -07:00
1 parent 715be25792
commit 877ce38015
7 files changed
+171 -86

No files matched your search

+6 -77
View File
@@ -16,14 +16,13 @@
"cron": "^4.3.1",
"debug": "~4.4.1",
"dockerode": "^5.0.1",
"ejs": "^3.1.10",
"ejs": "^6.0.1",
"express": "^5.1.0",
"fs-extra": "^11.3.0",
"http-proxy": "^1.18.1",
"is-valid-path": "^0.1.1",
"js-base64": "^3.7.7",
"jsonwebtoken": "^9.0.2",
"moment": "^2.30.1",
"morgan": "^1.10.0",
"multer": "^2.0.1",
"on-finished": "^2.4.1",
@@ -3404,12 +3403,6 @@
"node": ">=18"
}
},
"node_modules/async": {
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
"integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
"license": "MIT"
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@@ -3564,12 +3557,6 @@
"@babel/core": "^7.11.0 || ^8.0.0-beta.1 || ^8.0.0"
}
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"license": "MIT"
},
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
@@ -4660,18 +4647,15 @@
"license": "MIT"
},
"node_modules/ejs": {
"version": "3.1.10",
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz",
"integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/ejs/-/ejs-6.0.1.tgz",
"integrity": "sha512-UaaM14yby8U3k02ihS1Bmj5Kz2d7CCQM1scxpgs4Mhkq8F1wR2gl3+Ts4h5Ne4Mnt7M9m4Dw7jsuMr3+xO4vZA==",
"license": "Apache-2.0",
"dependencies": {
"jake": "^10.8.5"
},
"bin": {
"ejs": "bin/cli.js"
},
"engines": {
"node": ">=0.10.0"
"node": ">=0.12.18"
}
},
"node_modules/electron-to-chromium": {
@@ -5220,36 +5204,6 @@
"flat-cache": "^6.1.23"
}
},
"node_modules/filelist": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz",
"integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==",
"license": "Apache-2.0",
"dependencies": {
"minimatch": "^5.0.1"
}
},
"node_modules/filelist/node_modules/brace-expansion": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz",
"integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==",
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0"
}
},
"node_modules/filelist/node_modules/minimatch": {
"version": "5.1.9",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz",
"integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==",
"license": "ISC",
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
"node": ">=10"
}
},
"node_modules/filing-cabinet": {
"version": "5.5.1",
"resolved": "https://registry.npmjs.org/filing-cabinet/-/filing-cabinet-5.5.1.tgz",
@@ -6246,23 +6200,6 @@
"@pkgjs/parseargs": "^0.11.0"
}
},
"node_modules/jake": {
"version": "10.9.4",
"resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz",
"integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==",
"license": "Apache-2.0",
"dependencies": {
"async": "^3.2.6",
"filelist": "^1.0.4",
"picocolors": "^1.1.1"
},
"bin": {
"jake": "bin/cli.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/jest": {
"version": "30.5.1",
"resolved": "https://registry.npmjs.org/jest/-/jest-30.5.1.tgz",
@@ -7353,15 +7290,6 @@
"node": ">=18"
}
},
"node_modules/moment": {
"version": "2.30.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
"license": "MIT",
"engines": {
"node": "*"
}
},
"node_modules/morgan": {
"version": "1.12.0",
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.12.0.tgz",
@@ -7862,6 +7790,7 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"dev": true,
"license": "ISC"
},
"node_modules/picomatch": {
+1 -2
View File
@@ -22,14 +22,13 @@
"cron": "^4.3.1",
"debug": "~4.4.1",
"dockerode": "^5.0.1",
"ejs": "^3.1.10",
"ejs": "^6.0.1",
"express": "^5.1.0",
"fs-extra": "^11.3.0",
"http-proxy": "^1.18.1",
"is-valid-path": "^0.1.1",
"js-base64": "^3.7.7",
"jsonwebtoken": "^9.0.2",
"moment": "^2.30.1",
"morgan": "^1.10.0",
"multer": "^2.0.1",
"on-finished": "^2.4.1",
+5 -5
View File
@@ -1,7 +1,6 @@
import SshClientImport = require('ssh2')
import { exec } from 'child_process'
import * as fs from 'fs-extra'
import moment from 'moment'
import * as path from 'path'
import * as tar from 'tar'
import ApiStatusCodes from '../../api/ApiStatusCodes'
@@ -12,6 +11,7 @@ import { BackupMeta, RestoringInfo } from '../../models/BackupMeta'
import { IHashMapGeneric } from '../../models/ICacheGeneric'
import { ServerDockerInfo } from '../../models/ServerDockerInfo'
import CaptainConstants from '../../utils/CaptainConstants'
import { formatBackupDate } from '../../utils/DateUtils'
import Logger from '../../utils/Logger'
import Utils from '../../utils/Utils'
import Authenticator from '../Authenticator'
@@ -738,12 +738,12 @@ export default class BackupManager {
// https://github.com/caprover/caprover/issues/1257
const hostSegment = hostname ? `-host-${hostname}` : ''
const now = moment()
const now = new Date()
const newName = `${
CaptainConstants.captainDownloadsDirectory
}/${namespace}/caprover-backup-${`${now.format(
'YYYY_MM_DD-HH_mm_ss'
)}-${now.valueOf()}`}${`-ip-${mainIP}${hostSegment}.tar`}`
}/${namespace}/caprover-backup-${`${formatBackupDate(
now
)}-${now.getTime()}`}${`-ip-${mainIP}${hostSegment}.tar`}`
fs.moveSync(tarFilePath, newName)
setTimeout(
+56
View File
@@ -0,0 +1,56 @@
const MONTHS = [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December',
]
function pad(value: number, length = 2) {
return value.toString().padStart(length, '0')
}
function getOrdinalSuffix(day: number) {
if (day >= 11 && day <= 13) {
return 'th'
}
switch (day % 10) {
case 1:
return 'st'
case 2:
return 'nd'
case 3:
return 'rd'
default:
return 'th'
}
}
export function formatLogDate(date: Date) {
const day = date.getDate()
const hours = date.getHours()
const twelveHour = hours % 12 || 12
const meridiem = hours < 12 ? 'am' : 'pm'
return `${MONTHS[date.getMonth()]} ${day}${getOrdinalSuffix(
day
)} ${date.getFullYear()}, ${twelveHour}:${pad(date.getMinutes())}:${pad(
date.getSeconds()
)}.${pad(date.getMilliseconds(), 3)} ${meridiem} `
}
export function formatBackupDate(date: Date) {
return `${date.getFullYear()}_${pad(date.getMonth() + 1)}_${pad(
date.getDate()
)}-${pad(date.getHours())}_${pad(date.getMinutes())}_${pad(
date.getSeconds()
)}`
}
+2 -2
View File
@@ -1,6 +1,6 @@
import moment from 'moment'
import { AnyError } from '../models/OtherTypes'
import CaptainConstants from './CaptainConstants'
import { formatLogDate } from './DateUtils'
function errorize(error: AnyError) {
if (!(error instanceof Error)) {
@@ -11,7 +11,7 @@ function errorize(error: AnyError) {
}
function getTime() {
return `${moment().format('MMMM Do YYYY, h:mm:ss.SSS a ')}`
return `${formatLogDate(new Date())}`
}
class Logger {
+27
View File
@@ -0,0 +1,27 @@
import { formatBackupDate, formatLogDate } from '../src/utils/DateUtils'
describe('DateUtils', () => {
test.each([
[1, 'January 1st 2026, 12:05:04.009 am '],
[2, 'January 2nd 2026, 12:05:04.009 am '],
[3, 'January 3rd 2026, 12:05:04.009 am '],
[11, 'January 11th 2026, 12:05:04.009 am '],
[12, 'January 12th 2026, 12:05:04.009 am '],
[13, 'January 13th 2026, 12:05:04.009 am '],
[21, 'January 21st 2026, 12:05:04.009 am '],
])('formats log timestamps for day %i', (day, expected) => {
expect(formatLogDate(new Date(2026, 0, day, 0, 5, 4, 9))).toBe(expected)
})
test('formats afternoon log timestamps', () => {
expect(formatLogDate(new Date(2026, 8, 7, 19, 5, 4, 9))).toBe(
'September 7th 2026, 7:05:04.009 pm '
)
})
test('formats backup timestamps', () => {
expect(formatBackupDate(new Date(2026, 8, 7, 19, 5, 4, 9))).toBe(
'2026_09_07-19_05_04'
)
})
})
+74
View File
@@ -0,0 +1,74 @@
import ejs = require('ejs')
import fs = require('fs')
import path = require('path')
const templates: [string, Record<string, unknown>][] = [
[
'base-nginx-conf.ejs',
{
base: { dhparamsFilePath: '/etc/nginx/dhparam.pem' },
},
],
[
'default-page.ejs',
{
message_title: 'CapRover',
message_body: 'Ready',
message_link: 'https://caprover.com',
message_link_title: 'Documentation',
},
],
[
'root-nginx-conf.ejs',
{
fake: { crtPath: '/fake.crt', keyPath: '/fake.key' },
captain: {
hasRootSsl: true,
crtPath: '/captain.crt',
keyPath: '/captain.key',
domain: 'captain.example.com',
serviceName: 'captain-captain',
serviceContainerPort3000: 3000,
staticWebRoot: '/captain/static',
defaultHtmlDir: '/captain/default',
},
registry: {
hasRootSsl: false,
domain: 'registry.example.com',
staticWebRoot: '/registry/static',
},
},
],
[
'server-block-conf.ejs',
{
s: {
forceSsl: true,
publicDomain: 'app.example.com',
staticWebRoot: '/app/static',
hasSsl: true,
crtPath: '/app.crt',
keyPath: '/app.key',
logAccessPath: '',
gzipOn: false,
localDomain: 'srv-captain--app',
containerHttpPort: 3000,
redirectToPath: '',
httpBasicAuthPath: '',
websocketSupport: true,
customErrorPagesDirectory: '/captain/error-pages',
},
},
],
]
describe('EJS nginx templates', () => {
test.each(templates)('renders %s', (filename, data) => {
const template = fs.readFileSync(
path.join(__dirname, '..', 'template', filename),
'utf8'
)
expect(ejs.render(template, data)).toBeTruthy()
})
})