Merge pull request #18316 from overleaf/jpa-host-admin

[server-ce] e2e tests: add capability for reconfiguring the instance

GitOrigin-RevId: 44ec800b5b37497b3122310a94f637c24ff2667a
This commit is contained in:
Jakob Ackermann 2024-05-30 09:12:56 +02:00 committed by Copybot
parent 22257cf037
commit 8eb8b233c0
19 changed files with 1144 additions and 177 deletions

View file

@ -1,3 +1,4 @@
.DS_Store
.git/
services/git-bridge
server-ce/test

View file

@ -15,11 +15,6 @@ ADD package.json package-lock.json /overleaf/
ADD libraries/ /overleaf/libraries/
ADD services/ /overleaf/services/
# Store the revision
# ------------------
ARG MONOREPO_REVISION
RUN echo "monorepo-server-ce,$MONOREPO_REVISION" > /var/www/revisions.txt
# Add npm patches
# -----------------------
ADD patches/ /overleaf/patches
@ -115,3 +110,9 @@ ENV LOG_LEVEL "info"
EXPOSE 80
ENTRYPOINT ["/sbin/my_init"]
# Store the revision
# ------------------
# This should be the last step to optimize docker image caching.
ARG MONOREPO_REVISION
RUN echo "monorepo-server-ce,$MONOREPO_REVISION" > /var/www/revisions.txt

View file

@ -0,0 +1,8 @@
FROM node:18.20.2
RUN curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add - \
&& echo \
"deb [arch=$(dpkg --print-architecture)] https://download.docker.com/linux/debian $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
> /etc/apt/sources.list.d/docker.list \
&& apt-get update \
&& apt-get install -y docker-ce-cli docker-compose-plugin \
&& rm -rf /var/lib/apt/lists/*

View file

@ -1,13 +1,22 @@
all: test-e2e
# We are updating the docker compose config via the host-admin service.
# The host-admin service is running inside docker and has its own file-system layout.
# We need to have both file-system layouts agree on the path for the docker compose project.
# Notable the container labels com.docker.compose.project.working_dir and com.docker.compose.project.config_files need to match when creating containers from the docker host (how you started things) and from host-admin (how tests reconfigure the instance).
export PWD = $(shell pwd)
test-e2e:
docker compose down -v -t 0
docker compose -f docker-compose.yml run --rm e2e
docker compose down -v -t 0
docker compose up --build --no-log-prefix --exit-code-from=e2e e2e
test-e2e-open:
docker compose down -v -t 0
docker compose -f docker-compose.yml run --rm e2e-open
docker compose down -v -t 0
docker compose up --build --no-log-prefix --exit-code-from=e2e-open e2e-open
clean:
docker compose down --volumes --timeout 0
prefetch:
docker compose pull e2e mongo redis
docker compose build
.PHONY: test-e2e test-e2e-open

View file

@ -1,6 +1,10 @@
import { login } from './helpers/login'
import { ensureUserExists, login } from './helpers/login'
import { startWith } from './helpers/config'
describe('Accounts', function () {
startWith({})
ensureUserExists({ email: 'user@example.com' })
it('can log in and out', function () {
login('user@example.com')
cy.visit('/project')

View file

@ -1,12 +1,17 @@
import { login } from './helpers/login'
import { ensureUserExists, login } from './helpers/login'
import { createProject } from './helpers/project'
import { startWith } from './helpers/config'
describe('Project creation and compilation', function () {
startWith({})
ensureUserExists({ email: 'user@example.com' })
ensureUserExists({ email: 'collaborator@example.com' })
it('users can create project and compile it', function () {
login('user@example.com')
cy.visit('/project')
// this is the first project created, the welcome screen is displayed instead of the project list
createProject('test-project', { isFirstProject: true })
createProject('test-project')
cy.url().should('match', /\/project\/[a-fA-F0-9]{24}/)
cy.findByText('\\maketitle').parent().click()
cy.findByText('\\maketitle').parent().type('\n\\section{{}Test Section}')

View file

@ -0,0 +1,25 @@
import { startWith } from './helpers/config'
describe('Customization', () => {
startWith({
vars: {
OVERLEAF_APP_NAME: 'CUSTOM APP NAME',
OVERLEAF_LEFT_FOOTER: JSON.stringify([{ text: 'CUSTOM LEFT FOOTER' }]),
OVERLEAF_RIGHT_FOOTER: JSON.stringify([{ text: 'CUSTOM RIGHT FOOTER' }]),
},
})
it('should display custom name', () => {
cy.visit('/')
cy.get('nav').findByText('CUSTOM APP NAME')
})
it('should display custom left footer', () => {
cy.visit('/')
cy.get('footer').findByText('CUSTOM LEFT FOOTER')
})
it('should display custom right footer', () => {
cy.visit('/')
cy.get('footer').findByText('CUSTOM RIGHT FOOTER')
})
})

View file

@ -3,8 +3,9 @@ const { defineConfig } = require('cypress')
const specPattern = process.env.SPEC_PATTERN || './**/*.spec.{js,ts,tsx}'
module.exports = defineConfig({
defaultCommandTimeout: 10_000,
fixturesFolder: 'cypress/fixtures',
video: !!process.env.CI,
video: process.env.CYPRESS_VIDEO === 'true',
screenshotsFolder: 'cypress/results',
videosFolder: 'cypress/results',
videoUploadOnPasses: false,

View file

@ -1 +1,2 @@
downloads/
results/

View file

@ -1,18 +1,18 @@
version: '2.2'
services:
sharelatex:
image: ${IMAGE_TAG:-sharelatex/sharelatex:latest}
container_name: sharelatex
image: ${IMAGE_TAG_CE:-sharelatex/sharelatex:latest}
# TODO(das7pad): increase timeout after speeding up the graceful shutdown procedure
# stop_grace_period: 60s
stop_grace_period: 0s
depends_on:
mongo:
condition: service_healthy
redis:
condition: service_started
ports:
- 127.0.0.2:80:80
links:
- mongo
- redis
host-admin:
# The host-admin service initiates the mongo replica set
condition: service_healthy
environment:
OVERLEAF_APP_NAME: Overleaf Community Edition
OVERLEAF_MONGO_URL: mongodb://mongo/sharelatex?directConnection=true
@ -23,36 +23,27 @@ services:
EMAIL_CONFIRMATION_DISABLED: 'true'
healthcheck:
test: curl --fail http://localhost:3000/status || exit 1
interval: 10s
interval: 3s
timeout: 10s
retries: 10
volumes:
- ./util/seed-mongo.sh:/etc/my_init.d/99_seed-mongo.sh
- ./util/seed-mongo.js:/overleaf/services/web/modules/server-ce-scripts/scripts/seed-mongo.js
mongo:
image: mongo:5.0.17
container_name: mongo
command: '--replSet overleaf'
expose:
- 27017
healthcheck:
# FIXME: silly hack to make sure replicaset is initialized
test: 'echo ''rs.initiate({ _id: "overleaf", members: [{ _id: 0, host: "mongo:27017" }] })'' | mongo localhost:27017/test --quiet'
test: echo 'db.stats().ok' | mongo localhost:27017/test --quiet
interval: 10s
timeout: 10s
retries: 5
redis:
image: redis:7.2.1
container_name: redis
expose:
- 6379
e2e:
image: cypress/included:13.6.6
links:
- sharelatex
stop_grace_period: 0s
entrypoint: npm
command: run cypress:run
working_dir: /e2e
volumes:
- ./:/e2e
@ -62,13 +53,14 @@ services:
depends_on:
sharelatex:
condition: service_healthy
host-admin:
condition: service_healthy
e2e-open:
image: cypress/included:13.6.6
stop_grace_period: 0s
entrypoint: npm
command: run cypress:open
links:
- sharelatex
working_dir: /e2e
volumes:
- ./:/e2e
@ -81,3 +73,27 @@ services:
depends_on:
sharelatex:
condition: service_healthy
host-admin:
condition: service_healthy
host-admin:
build: .
entrypoint: ["node", "--watch", "host-admin.js"]
# See comment in Makefile regarding matching file paths
working_dir: $PWD
volumes:
- $PWD:$PWD
- /var/run/docker.sock:/var/run/docker.sock
stop_grace_period: 0s
environment:
PWD:
IMAGE_TAG_CE: ${IMAGE_TAG_CE:-sharelatex/sharelatex:latest}
IMAGE_TAG_PRO: ${IMAGE_TAG_PRO:-quay.io/sharelatex/sharelatex-pro:latest}
depends_on:
mongo:
condition: service_healthy
healthcheck:
test: curl --fail http://localhost/status || exit 1
interval: 3s
timeout: 10s
retries: 10

View file

@ -0,0 +1,16 @@
import { reconfigure } from './hostAdminClient'
let lastConfig: string
export function startWith({ pro = false, version = 'latest', vars = {} }) {
before(async function () {
const cfg = JSON.stringify({ pro, version, vars })
if (lastConfig === cfg) return
this.timeout(100 * 1000)
await reconfigure({ pro, version, vars })
lastConfig = cfg
})
}
export { reconfigure }

View file

@ -0,0 +1,84 @@
export async function setVars(vars = {}) {
return await fetchJSON('http://host-admin/set/vars', {
method: 'POST',
body: JSON.stringify({ vars, path: 'docker-compose.yml' }),
})
}
export async function setVersion({ pro = false, version = 'latest' }) {
return await fetchJSON('http://host-admin/set/version', {
method: 'POST',
body: JSON.stringify({
pro,
version,
path: 'docker-compose.yml',
}),
})
}
export async function dockerCompose(cmd: string, ...args: string[]) {
return await fetchJSON(`http://host-admin/docker/compose/${cmd}`, {
method: 'POST',
body: JSON.stringify({
args,
}),
})
}
export async function mongoInit() {
return await fetchJSON('http://host-admin/mongo/init', {
method: 'POST',
})
}
export async function reconfigure({
pro = false,
version = 'latest',
vars = {},
}) {
return await fetchJSON('http://host-admin/reconfigure', {
method: 'POST',
body: JSON.stringify({
pro,
version,
vars,
}),
})
}
async function fetchJSON(
input: RequestInfo,
init?: RequestInit
): Promise<{ stdout: string; stderr: string }> {
if (init?.body) {
init.headers = { 'Content-Type': 'application/json' }
}
const res = await fetch(input, init)
const { error, stdout, stderr } = await res.json()
if (error) {
console.error(input, init, 'failed:', error)
const err = new Error(error.message)
Object.assign(err, error)
throw err
}
return { stdout, stderr }
}
export async function runScript({
cwd,
script,
args = [],
}: {
cwd: string
script: string
args?: string[]
}) {
return await fetchJSON('http://host-admin/run/script', {
method: 'POST',
body: JSON.stringify({
cwd,
script,
args,
}),
})
}

View file

@ -1,4 +1,57 @@
export function login(username: string, password = 'Passw0rd!') {
import { runScript } from './hostAdminClient'
const DEFAULT_PASSWORD = 'Passw0rd!'
const createdUsers = new Set<string>()
async function createMongoUser({
email,
isAdmin = false,
}: {
email: string
isAdmin?: boolean
}) {
const t0 = Date.now()
const { stdout } = await runScript({
cwd: 'services/web',
script: 'modules/server-ce-scripts/scripts/create-user.js',
args: [`--email=${email}`, `--admin=${isAdmin}`],
})
const [url] = stdout.match(/\/user\/activate\?token=\S+/)!
const userId = new URL(url, location.origin).searchParams.get('user_id')!
const signupDate = parseInt(userId.slice(0, 8), 16) * 1000
if (signupDate < t0) {
return { url, exists: true }
}
return { url, exists: false }
}
export function ensureUserExists({
email,
password = DEFAULT_PASSWORD,
isAdmin = false,
}: {
email: string
password?: string
isAdmin?: boolean
}) {
let url: string
let exists: boolean
before(async function () {
exists = createdUsers.has(email)
if (exists) return
;({ url, exists } = await createMongoUser({ email, isAdmin }))
})
before(function () {
if (exists) return
activateUser(url, password)
cy.then(() => {
createdUsers.add(email)
})
})
}
export function login(username: string, password = DEFAULT_PASSWORD) {
cy.session([username, password, new Date()], () => {
cy.visit('/login')
cy.get('input[name="email"]').type(username)
@ -7,3 +60,16 @@ export function login(username: string, password = 'Passw0rd!') {
cy.url().should('contain', '/project')
})
}
export function activateUser(url: string, password = DEFAULT_PASSWORD) {
cy.session(url, () => {
cy.visit(url)
cy.url().then(url => {
if (url.includes('/login')) return
cy.url().should('contain', '/user/activate')
cy.get('input[name="password"]').type(password)
cy.findByRole('button', { name: 'Activate' }).click()
cy.url().should('contain', '/project')
})
})
}

View file

@ -2,18 +2,13 @@ export function createProject(
name: string,
{
type = 'Blank Project',
isFirstProject,
}: {
type?: 'Blank Project' | 'Example Project'
isFirstProject?: boolean
} = {}
): Cypress.Chainable<string> {
if (isFirstProject) {
cy.findByText('Create a new project').click()
} else {
// FIXME: This should be be a data-test-id shared between the welcome page and project list
cy.get('.new-project-button').first().click()
}
cy.findAllByRole('button')
.contains(/new project/i)
.click()
// FIXME: This should only look in the left menu
cy.findAllByText(type).first().click()
cy.findByRole('dialog').within(() => {

View file

@ -0,0 +1,279 @@
const fs = require('fs')
const { execFile } = require('child_process')
const express = require('express')
const bodyParser = require('body-parser')
const {
celebrate: validate,
Joi,
errors: handleValidationErrors,
} = require('celebrate')
const YAML = require('js-yaml')
const FILES = {
DOCKER_COMPOSE: 'docker-compose.override.yml',
}
const IMAGES = {
CE: process.env.IMAGE_TAG_CE.replace(/:.+/, ''),
PRO: process.env.IMAGE_TAG_PRO.replace(/:.+/, ''),
}
let mongoIsInitialized = false
function readDockerComposeOverride() {
try {
return YAML.load(fs.readFileSync(FILES.DOCKER_COMPOSE, 'utf-8'))
} catch (error) {
if (error.code !== 'ENOENT') {
throw error
}
return {
services: {
sharelatex: {
environment: {},
},
},
}
}
}
function writeDockerComposeOverride(cfg) {
fs.writeFileSync(FILES.DOCKER_COMPOSE, YAML.dump(cfg))
}
const app = express()
app.get('/status', (req, res) => {
res.send('host-admin is up')
})
app.use(bodyParser.json())
app.use((req, res, next) => {
// Basic access logs
console.log(req.method, req.url, req.body)
// Add CORS headers
res.setHeader('Access-Control-Allow-Origin', 'http://sharelatex')
res.setHeader('Access-Control-Allow-Headers', 'Content-Type')
next()
})
app.post(
'/run/script',
validate(
{
body: {
cwd: Joi.string().required(),
script: Joi.string().required(),
args: Joi.array().items(Joi.string()),
},
},
{ allowUnknown: false }
),
(req, res) => {
const { cwd, script, args } = req.body
execFile(
'docker',
[
'compose',
'exec',
'sharelatex',
'bash',
'-c',
`source /etc/container_environment.sh && source /etc/overleaf/env.sh && cd ${JSON.stringify(cwd)} && node ${JSON.stringify(script)} ${args.map(a => JSON.stringify(a)).join(' ')}`,
],
(error, stdout, stderr) => {
res.json({
error,
stdout,
stderr,
})
}
)
}
)
function setVersionDockerCompose({ pro, version }) {
const cfg = readDockerComposeOverride()
cfg.services.sharelatex.image = `${pro ? IMAGES.PRO : IMAGES.CE}:${version}`
writeDockerComposeOverride(cfg)
}
app.post(
'/set/version',
validate(
{
body: {
pro: Joi.boolean(),
version: Joi.string().required(),
path: Joi.allow(
'docker-compose.yml'
// When extending testing for Toolkit:
// 'config/version'
),
},
},
{ allowUnknown: false }
),
(req, res) => {
const { pro, version } = req.body
if (req.body.path === 'docker-compose.yml') {
try {
setVersionDockerCompose({ pro, version })
} catch (error) {
return res.json({ error })
}
}
res.json({})
}
)
const allowedVars = Joi.object().keys({
OVERLEAF_APP_NAME: Joi.string(),
OVERLEAF_LEFT_FOOTER: Joi.string(),
OVERLEAF_RIGHT_FOOTER: Joi.string(),
})
function setVarsDockerCompose({ vars }) {
const cfg = readDockerComposeOverride()
cfg.services.sharelatex.environment = vars
writeDockerComposeOverride(cfg)
}
app.post(
'/set/vars',
validate(
{
body: {
vars: allowedVars,
path: Joi.allow(
'docker-compose.yml'
// When extending the testing for Toolkit:
// 'overleaf.rc', 'variables.env'
),
},
},
{ allowUnknown: false }
),
(req, res) => {
if (req.body.path === 'docker-compose.yml') {
const { vars } = req.body
try {
setVarsDockerCompose({ vars })
} catch (error) {
return res.json({ error })
}
}
res.json({})
}
)
app.post(
'/docker/compose/:cmd',
validate(
{
body: {
args: Joi.array().allow(
'--detach',
'--wait',
'--volumes',
'--timeout',
'0',
'sharelatex',
'mongo',
'redis'
),
},
params: {
cmd: Joi.allow('up', 'stop', 'down', 'ps', 'logs'),
},
},
{ allowUnknown: false }
),
(req, res) => {
const { cmd } = req.params
const { args } = req.body
if (['stop', 'down'].includes(cmd)) {
mongoIsInitialized = false
}
execFile('docker', ['compose', cmd, ...args], (error, stdout, stderr) => {
res.json({ error, stdout, stderr })
})
}
)
function mongoInit(callback) {
execFile(
'docker',
[
'compose',
'exec',
'mongo',
'mongo',
'--eval',
'rs.initiate({ _id: "overleaf", members: [ { _id: 0, host: "mongo:27017" } ] })',
],
(error, stdout, stderr) => {
if (!error) {
mongoIsInitialized = true
}
callback(error, stdout, stderr)
}
)
}
app.post('/mongo/init', (req, res) => {
mongoInit((error, stdout, stderr) => {
res.json({ error, stdout, stderr })
})
})
app.post(
'/reconfigure',
validate(
{
body: {
pro: Joi.boolean().required(),
version: Joi.string().required(),
vars: allowedVars,
},
},
{ allowUnknown: false }
),
(req, res) => {
const doMongoInit = mongoIsInitialized ? cb => cb() : mongoInit
doMongoInit((error, stdout, stderr) => {
if (error) return res.json({ error, stdout, stderr })
const { pro, version, vars } = req.body
try {
setVersionDockerCompose({ pro, version })
setVarsDockerCompose({ vars })
} catch (error) {
return res.json({ error })
}
execFile(
'docker',
['compose', 'up', '--detach', '--wait', 'sharelatex'],
(error, stdout, stderr) => {
res.json({ error, stdout, stderr })
}
)
})
}
)
app.use(handleValidationErrors())
// Init on startup
mongoInit(err => {
if (err) {
console.error('mongo init failed', err)
process.exit(1)
}
app.listen(80)
})

View file

@ -7,7 +7,11 @@
"name": "@overleaf/server-ce/test",
"dependencies": {
"@testing-library/cypress": "^10.0.1",
"body-parser": "^1.20.2",
"celebrate": "^15.0.3",
"cypress": "13.6.6",
"express": "^4.19.2",
"js-yaml": "^4.1.0",
"typescript": "^5.0.4"
}
},
@ -221,6 +225,37 @@
"ms": "^2.1.1"
}
},
"node_modules/@hapi/hoek": {
"version": "9.3.0",
"resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz",
"integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ=="
},
"node_modules/@hapi/topo": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz",
"integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==",
"dependencies": {
"@hapi/hoek": "^9.0.0"
}
},
"node_modules/@sideway/address": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz",
"integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==",
"dependencies": {
"@hapi/hoek": "^9.0.0"
}
},
"node_modules/@sideway/formula": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz",
"integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg=="
},
"node_modules/@sideway/pinpoint": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz",
"integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ=="
},
"node_modules/@testing-library/cypress": {
"version": "10.0.1",
"resolved": "https://registry.npmjs.org/@testing-library/cypress/-/cypress-10.0.1.tgz",
@ -288,6 +323,18 @@
"@types/node": "*"
}
},
"node_modules/accepts": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
"dependencies": {
"mime-types": "~2.1.34",
"negotiator": "0.6.3"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/aggregate-error": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
@ -363,6 +410,11 @@
}
]
},
"node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
},
"node_modules/aria-query": {
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz",
@ -383,6 +435,11 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
},
"node_modules/asn1": {
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
@ -491,6 +548,56 @@
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
"integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="
},
"node_modules/body-parser": {
"version": "1.20.2",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
"integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
"dependencies": {
"bytes": "3.1.2",
"content-type": "~1.0.5",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.11.0",
"raw-body": "2.5.2",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/body-parser/node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/body-parser/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"node_modules/body-parser/node_modules/qs": {
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
"dependencies": {
"side-channel": "^1.0.4"
},
"engines": {
"node": ">=0.6"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@ -531,6 +638,14 @@
"node": "*"
}
},
"node_modules/bytes": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/cachedir": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.4.0.tgz",
@ -557,6 +672,16 @@
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
"integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw=="
},
"node_modules/celebrate": {
"version": "15.0.3",
"resolved": "https://registry.npmjs.org/celebrate/-/celebrate-15.0.3.tgz",
"integrity": "sha512-ToF8ILq/F0KhQ0CPtexP7Cu9GkqKJ91VKy3ZOCV24aaNWdm3QCHqnXAKfKHrtcM2B2zmPFe11p8WWsQkmq8k4g==",
"dependencies": {
"escape-html": "1.0.3",
"joi": "17.x.x",
"lodash": "4.17.x"
}
},
"node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@ -695,6 +820,38 @@
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
},
"node_modules/content-disposition": {
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
"integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
"dependencies": {
"safe-buffer": "5.2.1"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/content-type": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cookie": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
},
"node_modules/core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
@ -880,6 +1037,23 @@
"node": ">=0.4.0"
}
},
"node_modules/depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/destroy": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/dom-accessibility-api": {
"version": "0.5.16",
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz",
@ -894,11 +1068,24 @@
"safer-buffer": "^2.1.0"
}
},
"node_modules/ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
},
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"node_modules/encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/end-of-stream": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
@ -938,6 +1125,11 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
},
"node_modules/escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
@ -946,6 +1138,14 @@
"node": ">=0.8.0"
}
},
"node_modules/etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/eventemitter2": {
"version": "6.4.7",
"resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.7.tgz",
@ -984,6 +1184,74 @@
"node": ">=4"
}
},
"node_modules/express": {
"version": "4.19.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
"integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==",
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
"body-parser": "1.20.2",
"content-disposition": "0.5.4",
"content-type": "~1.0.4",
"cookie": "0.6.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "2.0.0",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "1.2.0",
"fresh": "0.5.2",
"http-errors": "2.0.0",
"merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.7",
"qs": "6.11.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.2.1",
"send": "0.18.0",
"serve-static": "1.15.0",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
},
"engines": {
"node": ">= 0.10.0"
}
},
"node_modules/express/node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/express/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"node_modules/express/node_modules/qs": {
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
"dependencies": {
"side-channel": "^1.0.4"
},
"engines": {
"node": ">=0.6"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
@ -1038,6 +1306,36 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/finalhandler": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
"integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
"dependencies": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
"statuses": "2.0.1",
"unpipe": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/finalhandler/node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/finalhandler/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"node_modules/for-each": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
@ -1067,6 +1365,22 @@
"node": ">= 0.12"
}
},
"node_modules/forwarded": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/fs-extra": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
@ -1269,6 +1583,21 @@
"node": ">= 0.4"
}
},
"node_modules/http-errors": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
"dependencies": {
"depd": "2.0.0",
"inherits": "2.0.4",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"toidentifier": "1.0.1"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/http-signature": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz",
@ -1290,6 +1619,17 @@
"node": ">=8.12.0"
}
},
"node_modules/iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
@ -1352,6 +1692,14 @@
"node": ">= 0.4"
}
},
"node_modules/ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
"engines": {
"node": ">= 0.10"
}
},
"node_modules/is-arguments": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
@ -1633,11 +1981,34 @@
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
"integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g=="
},
"node_modules/joi": {
"version": "17.13.1",
"resolved": "https://registry.npmjs.org/joi/-/joi-17.13.1.tgz",
"integrity": "sha512-vaBlIKCyo4FCUtCm7Eu4QZd/q02bWcxfUO6YSXAZOWF6gzcLBeba8kwotUdYJjDLW8Cz8RywsSOqiNJZW0mNvg==",
"dependencies": {
"@hapi/hoek": "^9.3.0",
"@hapi/topo": "^5.1.0",
"@sideway/address": "^4.1.5",
"@sideway/formula": "^3.0.1",
"@sideway/pinpoint": "^2.0.0"
}
},
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
"node_modules/js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"dependencies": {
"argparse": "^2.0.1"
},
"bin": {
"js-yaml": "bin/js-yaml.js"
}
},
"node_modules/jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
@ -1802,11 +2173,43 @@
"lz-string": "bin/bin.js"
}
},
"node_modules/media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
"integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
},
"node_modules/merge-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
"integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="
},
"node_modules/methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
"bin": {
"mime": "cli.js"
},
"engines": {
"node": ">=4"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
@ -1858,6 +2261,14 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"node_modules/negotiator": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/npm-run-path": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
@ -1917,6 +2328,17 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/on-finished": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
"dependencies": {
"ee-first": "1.1.1"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@ -1958,6 +2380,14 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
@ -1974,6 +2404,11 @@
"node": ">=8"
}
},
"node_modules/path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
},
"node_modules/pend": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
@ -2035,6 +2470,18 @@
"node": ">= 0.6.0"
}
},
"node_modules/proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
"dependencies": {
"forwarded": "0.2.0",
"ipaddr.js": "1.9.1"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/proxy-from-env": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz",
@ -2081,6 +2528,28 @@
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ=="
},
"node_modules/range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/raw-body": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
"dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/react-is": {
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
@ -2197,6 +2666,61 @@
"node": ">=10"
}
},
"node_modules/send": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
"integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
"dependencies": {
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "2.0.0",
"mime": "1.6.0",
"ms": "2.1.3",
"on-finished": "2.4.1",
"range-parser": "~1.2.1",
"statuses": "2.0.1"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/send/node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/send/node_modules/debug/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"node_modules/send/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
},
"node_modules/serve-static": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
"integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
"dependencies": {
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
"send": "0.18.0"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/set-function-length": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz",
@ -2224,6 +2748,11 @@
"node": ">= 0.4"
}
},
"node_modules/setprototypeof": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
},
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@ -2298,6 +2827,14 @@
"node": ">=0.10.0"
}
},
"node_modules/statuses": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/stop-iteration-iterator": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz",
@ -2373,6 +2910,14 @@
"node": ">=8.17.0"
}
},
"node_modules/toidentifier": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
"engines": {
"node": ">=0.6"
}
},
"node_modules/tough-cookie": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz",
@ -2427,6 +2972,18 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"dependencies": {
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/typescript": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
@ -2453,6 +3010,14 @@
"node": ">= 10.0.0"
}
},
"node_modules/unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/untildify": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz",
@ -2470,6 +3035,14 @@
"requires-port": "^1.0.0"
}
},
"node_modules/utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
@ -2478,6 +3051,14 @@
"uuid": "dist/bin/uuid"
}
},
"node_modules/vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/verror": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",

View file

@ -10,7 +10,11 @@
},
"dependencies": {
"@testing-library/cypress": "^10.0.1",
"body-parser": "^1.20.2",
"celebrate": "^15.0.3",
"cypress": "13.6.6",
"express": "^4.19.2",
"js-yaml": "^4.1.0",
"typescript": "^5.0.4"
}
}

View file

@ -1,121 +0,0 @@
const { ObjectId } = require('mongodb')
const { waitForDb, db } = require('../../../app/src/infrastructure/mongodb')
waitForDb()
.then(async () => {
await seedUsers()
process.exit(0)
})
.catch(err => {
console.error(err)
process.exit(1)
})
const DEFAULT_USER_PROPERTIES = {
staffAccess: {
publisherMetrics: false,
publisherManagement: false,
institutionMetrics: false,
institutionManagement: false,
groupMetrics: false,
groupManagement: false,
adminMetrics: false,
splitTestMetrics: false,
splitTestManagement: false,
},
ace: {
mode: 'none',
theme: 'textmate',
overallTheme: '',
fontSize: 12,
autoComplete: true,
autoPairDelimiters: true,
spellCheckLanguage: 'en',
pdfViewer: 'pdfjs',
syntaxValidation: true,
},
features: {
collaborators: -1,
versioning: true,
dropbox: true,
github: true,
gitBridge: true,
compileTimeout: 180,
compileGroup: 'standard',
templates: true,
references: true,
trackChanges: true,
},
first_name: 'user',
role: '',
institution: '',
isAdmin: false,
lastLoginIp: '',
loginCount: 0,
holdingAccount: false,
must_reconfirm: false,
refered_users: [],
refered_user_count: 0,
alphaProgram: false,
betaProgram: false,
labsProgram: false,
awareOfV2: false,
samlIdentifiers: [],
thirdPartyIdentifiers: [],
signUpDate: new Date('2023-11-02T11:36:40.151Z'),
featuresOverrides: [],
referal_id: 'scTS4kjjJENbfbjG',
__v: 0,
hashedPassword:
'$2a$12$nRvTj6U896uUnE.RFhnGKOyi/CvqBpfxezlqwyIPpezRa2xXLW7MO',
}
async function seedUsers() {
const adminUser = {
...DEFAULT_USER_PROPERTIES,
email: 'admin@example.com',
first_name: 'admin',
isAdmin: true,
emails: [
{
email: 'admin@example.com',
reversedHostname: '',
_id: ObjectId('646ca54806d54400b74e77c6'),
createdAt: new Date('2023-05-23T11:36:40.494Z'),
},
],
}
const user = {
...DEFAULT_USER_PROPERTIES,
_id: ObjectId('6543cf90bbe1368944db04d7'),
emails: [
{
email: 'user@example.com',
reversedHostname: '',
_id: ObjectId('6543c614d58b090b461f3549'),
createdAt: new Date('2023-11-21T11:36:40.494Z'),
},
],
email: 'user@example.com',
}
const collaborator = {
...DEFAULT_USER_PROPERTIES,
_id: ObjectId('6544e78c9b6e937424976b64'),
emails: [
{
email: 'collaborator@example.com',
reversedHostname: '',
_id: ObjectId('6543c614d58b090b461f354a'),
createdAt: new Date('2023-11-21T11:36:40.494Z'),
},
],
email: 'collaborator@example.com',
}
await db.users.insertOne(adminUser)
await db.users.insertOne(user)
await db.users.insertOne(collaborator)
}

View file

@ -1,8 +0,0 @@
#!/bin/sh
set -e
echo "Seeding mongo for e2e tests"
cd /overleaf/services/web
node modules/server-ce-scripts/scripts/seed-mongo
node modules/server-ce-scripts/scripts/check-redis
echo "mongo seeding complete"