mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-11-14 06:03:45 +01:00
Convert to ESM
GitOrigin-RevId: bc407a128b024792a65b62c2eaeacefa48ecfe8d
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
const bodyParser = require('body-parser')
|
||||
const HttpErrorHandler = require('../Features/Errors/HttpErrorHandler')
|
||||
import bodyParser from 'body-parser'
|
||||
import HttpErrorHandler from '../Features/Errors/HttpErrorHandler.js'
|
||||
|
||||
function isBodyParserError(nextArg) {
|
||||
if (nextArg instanceof Error) {
|
||||
@@ -29,7 +29,7 @@ const wrapBodyParser = method => opts => {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
urlencoded: wrapBodyParser('urlencoded'),
|
||||
json: wrapBodyParser('json'),
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const crypto = require('crypto')
|
||||
const path = require('path')
|
||||
import crypto from 'node:crypto'
|
||||
import path from 'node:path'
|
||||
|
||||
module.exports = function ({
|
||||
export default function ({
|
||||
reportUri,
|
||||
reportPercentage,
|
||||
reportOnly = false,
|
||||
@@ -57,7 +57,7 @@ module.exports = function ({
|
||||
}
|
||||
}
|
||||
|
||||
const buildDefaultPolicy = (reportUri, styleSrc) => {
|
||||
export const buildDefaultPolicy = (reportUri, styleSrc) => {
|
||||
const directives = [
|
||||
`base-uri 'none'`, // forbid setting a "base" element
|
||||
`default-src 'none'`, // forbid loading anything from a "src" attribute
|
||||
@@ -104,7 +104,7 @@ const buildViewPolicy = (
|
||||
return directives.join('; ')
|
||||
}
|
||||
|
||||
const webRoot = path.resolve(__dirname, '..', '..', '..')
|
||||
const webRoot = path.resolve(import.meta.dirname, '..', '..', '..')
|
||||
|
||||
// build the view path relative to the web root
|
||||
function relativeViewPath(view) {
|
||||
@@ -113,7 +113,7 @@ function relativeViewPath(view) {
|
||||
: path.join('app', 'views', view)
|
||||
}
|
||||
|
||||
function removeCSPHeaders(res) {
|
||||
export function removeCSPHeaders(res) {
|
||||
res.removeHeader('Content-Security-Policy')
|
||||
res.removeHeader('Content-Security-Policy-Report-Only')
|
||||
}
|
||||
@@ -122,13 +122,9 @@ function removeCSPHeaders(res) {
|
||||
* WARNING: allowing inline styles can open a security hole;
|
||||
* this is intended only for use in specific circumstances, such as Safari's built-in PDF viewer.
|
||||
*/
|
||||
function allowUnsafeInlineStyles(res) {
|
||||
export function allowUnsafeInlineStyles(res) {
|
||||
res.set(
|
||||
'Content-Security-Policy',
|
||||
buildDefaultPolicy(undefined, "'unsafe-inline'")
|
||||
)
|
||||
}
|
||||
|
||||
module.exports.buildDefaultPolicy = buildDefaultPolicy
|
||||
module.exports.removeCSPHeaders = removeCSPHeaders
|
||||
module.exports.allowUnsafeInlineStyles = allowUnsafeInlineStyles
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const Settings = require('@overleaf/settings')
|
||||
const metrics = require('@overleaf/metrics')
|
||||
import Settings from '@overleaf/settings'
|
||||
import metrics from '@overleaf/metrics'
|
||||
|
||||
/**
|
||||
* Middleware function to record session cookie metrics. This allows us to
|
||||
@@ -27,4 +27,4 @@ function middleware(req, res, next) {
|
||||
next()
|
||||
}
|
||||
|
||||
module.exports = { middleware }
|
||||
export default { middleware }
|
||||
|
||||
@@ -1,22 +1,9 @@
|
||||
/* eslint-disable
|
||||
max-len,
|
||||
no-return-assign,
|
||||
no-unused-vars,
|
||||
*/
|
||||
// TODO: This file was created by bulk-decaffeinate.
|
||||
// Fix any style issues and re-enable lint.
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
import csurf from 'csurf'
|
||||
import { promisify } from 'node:util'
|
||||
import Settings from '@overleaf/settings'
|
||||
import logger from '@overleaf/logger'
|
||||
|
||||
const csurf = require('csurf')
|
||||
const csrf = csurf()
|
||||
const { promisify } = require('util')
|
||||
const Settings = require('@overleaf/settings')
|
||||
const logger = require('@overleaf/logger')
|
||||
|
||||
// Wrapper for `csurf` middleware that provides a list of routes that can be excluded from csrf checks.
|
||||
//
|
||||
@@ -53,7 +40,7 @@ class Csrf {
|
||||
if (!this.excluded_routes[route]) {
|
||||
this.excluded_routes[route] = {}
|
||||
}
|
||||
return (this.excluded_routes[route][method] = 1)
|
||||
this.excluded_routes[route][method] = 1
|
||||
}
|
||||
|
||||
middleware(req, res, next) {
|
||||
@@ -62,21 +49,17 @@ class Csrf {
|
||||
// token' error from csurf and continue on...
|
||||
|
||||
// check whether the request method is excluded for the specified route
|
||||
if (
|
||||
(this.excluded_routes[req.path] != null
|
||||
? this.excluded_routes[req.path][req.method]
|
||||
: undefined) === 1
|
||||
) {
|
||||
if (this.excluded_routes[req.path]?.[req.method] === 1) {
|
||||
// ignore the error if it's due to a bad csrf token, and continue
|
||||
return csrf(req, res, err => {
|
||||
csrf(req, res, err => {
|
||||
if (err && err.code !== 'EBADCSRFTOKEN') {
|
||||
return next(err)
|
||||
next(err)
|
||||
} else {
|
||||
return next()
|
||||
next()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
return csrf(req, res, next)
|
||||
csrf(req, res, next)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +68,7 @@ class Csrf {
|
||||
if (cb == null) {
|
||||
cb = function (valid) {}
|
||||
}
|
||||
return csrf(req, null, err => cb(err))
|
||||
csrf(req, null, err => cb(err))
|
||||
}
|
||||
|
||||
static validateToken(token, session, cb) {
|
||||
@@ -102,7 +85,7 @@ class Csrf {
|
||||
method: 'POST',
|
||||
session,
|
||||
}
|
||||
return Csrf.validateRequest(req, cb)
|
||||
Csrf.validateRequest(req, cb)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,4 +94,4 @@ Csrf.promises = {
|
||||
validateToken: promisify(Csrf.validateToken),
|
||||
}
|
||||
|
||||
module.exports = Csrf
|
||||
export default Csrf
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
const session = require('express-session')
|
||||
const RedisStore = require('connect-redis')(session)
|
||||
const metrics = require('@overleaf/metrics')
|
||||
const logger = require('@overleaf/logger')
|
||||
const Settings = require('@overleaf/settings')
|
||||
const SessionManager = require('../Features/Authentication/SessionManager')
|
||||
const Metrics = require('@overleaf/metrics')
|
||||
import session from 'express-session'
|
||||
import RedisStoreFactory from 'connect-redis'
|
||||
import logger from '@overleaf/logger'
|
||||
import Settings from '@overleaf/settings'
|
||||
import SessionManager from '../Features/Authentication/SessionManager.js'
|
||||
import Metrics from '@overleaf/metrics'
|
||||
|
||||
const RedisStore = RedisStoreFactory(session)
|
||||
|
||||
const MAX_SESSION_SIZE_THRESHOLD = 4096
|
||||
|
||||
@@ -37,14 +38,14 @@ class CustomSessionStore extends RedisStore {
|
||||
}
|
||||
const size = sess ? JSON.stringify(sess).length : 0
|
||||
// record the number of redis session operations
|
||||
metrics.inc('session.store.count', 1, {
|
||||
Metrics.inc('session.store.count', 1, {
|
||||
method,
|
||||
type,
|
||||
status: size > MAX_SESSION_SIZE_THRESHOLD ? 'oversize' : 'normal',
|
||||
})
|
||||
// record the redis session bandwidth for get/set operations
|
||||
if (method === 'get' || method === 'set') {
|
||||
metrics.count('session.store.bytes', size, { method, type })
|
||||
Metrics.count('session.store.bytes', size, { method, type })
|
||||
}
|
||||
// log the largest anonymous session seen so far
|
||||
if (type === 'anonymous' && size > CustomSessionStore.largestSessionSize) {
|
||||
@@ -153,7 +154,7 @@ class CustomSetRedisClient {
|
||||
set(args, cb) {
|
||||
args.push(this.#flag)
|
||||
this.#client.set(args, (err, ok) => {
|
||||
metrics.inc('session.store.set', 1, {
|
||||
Metrics.inc('session.store.set', 1, {
|
||||
path: this.#flag,
|
||||
status: err ? 'error' : ok ? 'success' : 'failure',
|
||||
})
|
||||
@@ -162,4 +163,4 @@ class CustomSetRedisClient {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = CustomSessionStore
|
||||
export default CustomSessionStore
|
||||
|
||||
@@ -1,36 +1,43 @@
|
||||
const logger = require('@overleaf/logger')
|
||||
const Metrics = require('@overleaf/metrics')
|
||||
const Settings = require('@overleaf/settings')
|
||||
const _ = require('lodash')
|
||||
const { URL } = require('url')
|
||||
const Path = require('path')
|
||||
const moment = require('moment')
|
||||
const { fetchJson } = require('@overleaf/fetch-utils')
|
||||
const contentDisposition = require('content-disposition')
|
||||
const Features = require('./Features')
|
||||
const SessionManager = require('../Features/Authentication/SessionManager')
|
||||
const PackageVersions = require('./PackageVersions')
|
||||
const Modules = require('./Modules')
|
||||
const Errors = require('../Features/Errors/Errors')
|
||||
const {
|
||||
import logger from '@overleaf/logger'
|
||||
import Metrics from '@overleaf/metrics'
|
||||
import Settings from '@overleaf/settings'
|
||||
import _ from 'lodash'
|
||||
import { URL } from 'node:url'
|
||||
import Path from 'node:path'
|
||||
import moment from 'moment'
|
||||
import { fetchJson } from '@overleaf/fetch-utils'
|
||||
import contentDisposition from 'content-disposition'
|
||||
import Features from './Features.js'
|
||||
import SessionManager from '../Features/Authentication/SessionManager.js'
|
||||
import PackageVersions from './PackageVersions.js'
|
||||
import Modules from './Modules.js'
|
||||
import Errors from '../Features/Errors/Errors.js'
|
||||
|
||||
import {
|
||||
canRedirectToAdminDomain,
|
||||
hasAdminAccess,
|
||||
useAdminCapabilities,
|
||||
useHasAdminCapability,
|
||||
} = require('../Features/Helpers/AdminAuthorizationHelper')
|
||||
const {
|
||||
addOptionalCleanupHandlerAfterDrainingConnections,
|
||||
} = require('./GracefulShutdown')
|
||||
const { sanitizeSessionUserForFrontEnd } = require('./FrontEndUser')
|
||||
} from '../Features/Helpers/AdminAuthorizationHelper.js'
|
||||
|
||||
import { addOptionalCleanupHandlerAfterDrainingConnections } from './GracefulShutdown.js'
|
||||
import { sanitizeSessionUserForFrontEnd } from './FrontEndUser.mjs'
|
||||
import { expressify } from '@overleaf/promise-utils'
|
||||
|
||||
const IEEE_BRAND_ID = Settings.ieeeBrandId
|
||||
|
||||
let webpackManifest
|
||||
function loadManifest() {
|
||||
async function loadManifest() {
|
||||
switch (process.env.NODE_ENV) {
|
||||
case 'production':
|
||||
/* eslint-disable import/no-unresolved */
|
||||
// Only load webpack manifest file in production.
|
||||
webpackManifest = require('../../../public/manifest.json')
|
||||
webpackManifest = (
|
||||
await import('../../../public/manifest.json', {
|
||||
with: { type: 'json' },
|
||||
})
|
||||
).default
|
||||
/* eslint-enable import/no-unresolved */
|
||||
break
|
||||
case 'development': {
|
||||
// In dev, fetch the manifest from the webpack container.
|
||||
@@ -76,8 +83,8 @@ function getWebpackAssets(entrypoint, section) {
|
||||
return webpackManifest.entrypoints[entrypoint].assets[section] || []
|
||||
}
|
||||
|
||||
module.exports = function (webRouter, privateApiRouter, publicApiRouter) {
|
||||
loadManifest()
|
||||
export default async function (webRouter, privateApiRouter, publicApiRouter) {
|
||||
await loadManifest()
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
// In the dev-env, delay requests until we fetched the manifest once.
|
||||
webRouter.use(function (req, res, next) {
|
||||
@@ -253,10 +260,14 @@ module.exports = function (webRouter, privateApiRouter, publicApiRouter) {
|
||||
next()
|
||||
})
|
||||
|
||||
webRouter.use(function (req, res, next) {
|
||||
res.locals.StringHelper = require('../Features/Helpers/StringHelper')
|
||||
next()
|
||||
})
|
||||
webRouter.use(
|
||||
expressify(async function (req, res, next) {
|
||||
res.locals.StringHelper = (
|
||||
await import('../Features/Helpers/StringHelper.js')
|
||||
).default
|
||||
next()
|
||||
})
|
||||
)
|
||||
|
||||
webRouter.use(function (req, res, next) {
|
||||
res.locals.csrfToken = req != null ? req.csrfToken() : undefined
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
function sanitizeSessionUserForFrontEnd(sessionUser) {
|
||||
export function sanitizeSessionUserForFrontEnd(sessionUser) {
|
||||
if (sessionUser != null) {
|
||||
return {
|
||||
email: sessionUser.email,
|
||||
@@ -9,7 +9,3 @@ function sanitizeSessionUserForFrontEnd(sessionUser) {
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
sanitizeSessionUserForFrontEnd,
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const settings = require('@overleaf/settings')
|
||||
const logger = require('@overleaf/logger')
|
||||
const { fetchJson } = require('@overleaf/fetch-utils')
|
||||
import settings from '@overleaf/settings'
|
||||
import logger from '@overleaf/logger'
|
||||
import { fetchJson } from '@overleaf/fetch-utils'
|
||||
|
||||
const DEFAULT_CURRENCY_CODE = 'USD'
|
||||
|
||||
@@ -104,7 +104,7 @@ async function getCurrencyCode(ip) {
|
||||
return { currencyCode, countryCode }
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
isValidCurrencyParam,
|
||||
promises: {
|
||||
getDetails,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const { callbackify, promisify } = require('util')
|
||||
const JWT = require('jsonwebtoken')
|
||||
const Settings = require('@overleaf/settings')
|
||||
import { callbackify, promisify } from 'node:util'
|
||||
import JWT from 'jsonwebtoken'
|
||||
import Settings from '@overleaf/settings'
|
||||
|
||||
const jwtSign = promisify(JWT.sign)
|
||||
|
||||
@@ -20,7 +20,7 @@ function getDecoded(token) {
|
||||
return decoded
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
sign: callbackify(sign),
|
||||
getDecoded,
|
||||
promises: {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// TODO: This file was created by bulk-decaffeinate.
|
||||
// Sanity-check the conversion and remove this comment.
|
||||
module.exports = {
|
||||
export default {
|
||||
queue: {
|
||||
web_to_tpds_http_requests: 'web_to_tpds_http_requests',
|
||||
tpds_to_web_http_requests: 'tpds_to_web_http_requests',
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
module.exports = {
|
||||
export default {
|
||||
user(user) {
|
||||
if (user == null) {
|
||||
return null
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
const Features = require('./Features')
|
||||
const Queues = require('./Queues')
|
||||
const UserOnboardingEmailManager = require('../Features/User/UserOnboardingEmailManager')
|
||||
const UserPostRegistrationAnalyticsManager = require('../Features/User/UserPostRegistrationAnalyticsManager')
|
||||
const FeaturesUpdater = require('../Features/Subscription/FeaturesUpdater')
|
||||
const {
|
||||
import Features from './Features.js'
|
||||
import Queues from './Queues.js'
|
||||
import UserOnboardingEmailManager from '../Features/User/UserOnboardingEmailManager.js'
|
||||
import UserPostRegistrationAnalyticsManager from '../Features/User/UserPostRegistrationAnalyticsManager.js'
|
||||
import FeaturesUpdater from '../Features/Subscription/FeaturesUpdater.js'
|
||||
|
||||
import {
|
||||
addOptionalCleanupHandlerBeforeStoppingTraffic,
|
||||
addRequiredCleanupHandlerBeforeDrainingConnections,
|
||||
} = require('./GracefulShutdown')
|
||||
const EmailHandler = require('../Features/Email/EmailHandler')
|
||||
const logger = require('@overleaf/logger')
|
||||
const OError = require('@overleaf/o-error')
|
||||
const Modules = require('./Modules')
|
||||
} from './GracefulShutdown.js'
|
||||
|
||||
import EmailHandler from '../Features/Email/EmailHandler.js'
|
||||
import logger from '@overleaf/logger'
|
||||
import OError from '@overleaf/o-error'
|
||||
import Modules from './Modules.js'
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
@@ -124,4 +126,4 @@ function registerCleanup(queue) {
|
||||
// Disconnect from redis is scheduled in queue setup.
|
||||
}
|
||||
|
||||
module.exports = { start, registerQueue }
|
||||
export default { start, registerQueue }
|
||||
|
||||
@@ -12,11 +12,12 @@
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
let RedirectManager
|
||||
const settings = require('@overleaf/settings')
|
||||
const { URL } = require('url')
|
||||
import settings from '@overleaf/settings'
|
||||
import { URL } from 'node:url'
|
||||
|
||||
module.exports = RedirectManager = {
|
||||
let RedirectManager
|
||||
|
||||
export default RedirectManager = {
|
||||
apply(webRouter) {
|
||||
return (() => {
|
||||
const result = []
|
||||
|
||||
@@ -3,7 +3,7 @@ import Settings from '@overleaf/settings'
|
||||
import logger from '@overleaf/logger'
|
||||
import metrics from '@overleaf/metrics'
|
||||
import Validation from './Validation.js'
|
||||
import csp from './CSP.mjs'
|
||||
import csp, { removeCSPHeaders } from './CSP.mjs'
|
||||
import Router from '../router.mjs'
|
||||
import helmet from 'helmet'
|
||||
import UserSessionsRedis from '../Features/User/UserSessionsRedis.js'
|
||||
@@ -125,7 +125,7 @@ webRouter.use(
|
||||
fileURLToPath(new URL('../../../public', import.meta.url)),
|
||||
{
|
||||
maxAge: STATIC_CACHE_AGE,
|
||||
setHeaders: csp.removeCSPHeaders,
|
||||
setHeaders: removeCSPHeaders,
|
||||
}
|
||||
)
|
||||
)
|
||||
@@ -257,7 +257,7 @@ if (Settings.cookieRollingSession) {
|
||||
}
|
||||
|
||||
webRouter.use(ReferalConnect.use)
|
||||
expressLocals(webRouter, privateApiRouter, publicApiRouter)
|
||||
await expressLocals(webRouter, privateApiRouter, publicApiRouter)
|
||||
webRouter.use(SessionAutostartMiddleware.invokeCallbackMiddleware)
|
||||
|
||||
webRouter.use(function checkIfSiteClosed(req, res, next) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const Settings = require('@overleaf/settings')
|
||||
const OError = require('@overleaf/o-error')
|
||||
import Settings from '@overleaf/settings'
|
||||
import OError from '@overleaf/o-error'
|
||||
|
||||
const botUserAgents = [
|
||||
'kube-probe',
|
||||
@@ -125,4 +125,4 @@ class SessionAutostartMiddleware {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SessionAutostartMiddleware
|
||||
export default SessionAutostartMiddleware
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
const logger = require('@overleaf/logger')
|
||||
const settings = require('@overleaf/settings')
|
||||
const fs = require('fs')
|
||||
const {
|
||||
import logger from '@overleaf/logger'
|
||||
import settings from '@overleaf/settings'
|
||||
import fs from 'node:fs'
|
||||
|
||||
import {
|
||||
addOptionalCleanupHandlerAfterDrainingConnections,
|
||||
addRequiredCleanupHandlerBeforeDrainingConnections,
|
||||
} = require('./GracefulShutdown')
|
||||
const Features = require('./Features')
|
||||
const UserHandler = require('../Features/User/UserHandler')
|
||||
const metrics = require('@overleaf/metrics')
|
||||
} from './GracefulShutdown.js'
|
||||
|
||||
import Features from './Features.js'
|
||||
import UserHandler from '../Features/User/UserHandler.js'
|
||||
import metrics from '@overleaf/metrics'
|
||||
|
||||
// Monitor a site maintenance file (e.g. /etc/site_status) periodically and
|
||||
// close the site if the file contents contain the string "closed".
|
||||
@@ -56,7 +58,7 @@ function publishActiveUsersMetric() {
|
||||
.catch(error => logger.error({ error }, 'error counting active users'))
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
initialise() {
|
||||
if (settings.enabledServices.includes('web') && statusFile) {
|
||||
logger.debug(
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
const i18n = require('i18next')
|
||||
const fsBackend = require('i18next-fs-backend')
|
||||
const middleware = require('i18next-http-middleware')
|
||||
const path = require('path')
|
||||
const Settings = require('@overleaf/settings')
|
||||
const { URL } = require('url')
|
||||
const pug = require('pug-runtime')
|
||||
const logger = require('@overleaf/logger')
|
||||
const SafeHTMLSubstitution = require('../Features/Helpers/SafeHTMLSubstitution')
|
||||
import i18n from 'i18next'
|
||||
import fsBackend from 'i18next-fs-backend'
|
||||
import middleware from 'i18next-http-middleware'
|
||||
import path from 'node:path'
|
||||
import Settings from '@overleaf/settings'
|
||||
import { URL } from 'node:url'
|
||||
import pug from 'pug-runtime'
|
||||
import logger from '@overleaf/logger'
|
||||
import SafeHTMLSubstitution from '../Features/Helpers/SafeHTMLSubstitution.js'
|
||||
|
||||
const fallbackLanguageCode = Settings.i18n.defaultLng || 'en'
|
||||
const availableLanguageCodes = []
|
||||
@@ -35,7 +35,7 @@ if (!availableLanguageCodes.includes(fallbackLanguageCode)) {
|
||||
if (process.argv.includes('--watch-locales')) {
|
||||
// Dummy imports for setting up watching of locales files.
|
||||
for (const lngCode of availableLanguageCodes) {
|
||||
require(`../../../locales/${lngCode}.json`)
|
||||
await import(`../../../locales/${lngCode}.json`, { with: { type: 'json' } })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ i18n
|
||||
.use(middleware.LanguageDetector)
|
||||
.init({
|
||||
backend: {
|
||||
loadPath: path.join(__dirname, '../../../locales/__lng__.json'),
|
||||
loadPath: path.join(import.meta.dirname, '../../../locales/__lng__.json'),
|
||||
},
|
||||
|
||||
// still using the v3 plural suffixes
|
||||
@@ -140,7 +140,7 @@ function setLangBasedOnDomainMiddleware(req, res, next) {
|
||||
// in direct usage
|
||||
i18n.translate = i18n.t
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
i18nMiddleware: middleware.handle(i18n),
|
||||
setLangBasedOnDomainMiddleware,
|
||||
i18n,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const Bowser = require('bowser')
|
||||
const Settings = require('@overleaf/settings')
|
||||
const Url = require('url')
|
||||
const { getSafeRedirectPath } = require('../Features/Helpers/UrlHelper')
|
||||
import Bowser from 'bowser'
|
||||
import Settings from '@overleaf/settings'
|
||||
import Url from 'node:url'
|
||||
import { getSafeRedirectPath } from '../Features/Helpers/UrlHelper.js'
|
||||
|
||||
function unsupportedBrowserMiddleware(req, res, next) {
|
||||
if (!Settings.unsupportedBrowsers) return next()
|
||||
@@ -44,7 +44,7 @@ function renderUnsupportedBrowserPage(req, res) {
|
||||
res.render('general/unsupported-browser', { fromURL })
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
renderUnsupportedBrowserPage,
|
||||
unsupportedBrowserMiddleware,
|
||||
}
|
||||
|
||||
@@ -1,192 +1,161 @@
|
||||
/* eslint-disable
|
||||
max-len,
|
||||
no-return-assign,
|
||||
no-unused-vars,
|
||||
*/
|
||||
// TODO: This file was created by bulk-decaffeinate.
|
||||
// Fix any style issues and re-enable lint.
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const { assert, expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
const modulePath = '../../../../app/src/infrastructure/Csrf.js'
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import sinon from 'sinon'
|
||||
const modulePath = '../../../../app/src/infrastructure/Csrf.mjs'
|
||||
|
||||
describe('Csrf', function () {
|
||||
beforeEach(function () {
|
||||
this.csurf_csrf = sinon
|
||||
beforeEach(async function (ctx) {
|
||||
ctx.csurf_csrf = sinon
|
||||
.stub()
|
||||
.callsArgWith(2, (this.err = { code: 'EBADCSRFTOKEN' }))
|
||||
this.Csrf = SandboxedModule.require(modulePath, {
|
||||
requires: {
|
||||
csurf: sinon.stub().returns(this.csurf_csrf),
|
||||
},
|
||||
})
|
||||
this.csrf = new this.Csrf()
|
||||
this.next = sinon.stub()
|
||||
this.path = '/foo/bar'
|
||||
this.req = {
|
||||
path: this.path,
|
||||
.callsArgWith(2, (ctx.err = { code: 'EBADCSRFTOKEN' }))
|
||||
|
||||
vi.doMock('csurf', () => ({
|
||||
default: sinon.stub().returns(ctx.csurf_csrf),
|
||||
}))
|
||||
|
||||
ctx.Csrf = (await import(modulePath)).default
|
||||
ctx.csrf = new ctx.Csrf()
|
||||
ctx.next = sinon.stub()
|
||||
ctx.path = '/foo/bar'
|
||||
ctx.req = {
|
||||
path: ctx.path,
|
||||
method: 'POST',
|
||||
}
|
||||
return (this.res = {})
|
||||
ctx.res = {}
|
||||
})
|
||||
|
||||
describe('the middleware', function () {
|
||||
describe('when there are no excluded routes', function () {
|
||||
it('passes the csrf error on', function () {
|
||||
this.csrf.middleware(this.req, this.res, this.next)
|
||||
return expect(this.next.calledWith(this.err)).to.equal(true)
|
||||
it('passes the csrf error on', function (ctx) {
|
||||
ctx.csrf.middleware(ctx.req, ctx.res, ctx.next)
|
||||
return expect(ctx.next.calledWith(ctx.err)).to.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when the route is excluded', function () {
|
||||
it('does not pass the csrf error on', function () {
|
||||
this.csrf.disableDefaultCsrfProtection(this.path, 'POST')
|
||||
this.csrf.middleware(this.req, this.res, this.next)
|
||||
return expect(this.next.calledWith(this.err)).to.equal(false)
|
||||
it('does not pass the csrf error on', function (ctx) {
|
||||
ctx.csrf.disableDefaultCsrfProtection(ctx.path, 'POST')
|
||||
ctx.csrf.middleware(ctx.req, ctx.res, ctx.next)
|
||||
return expect(ctx.next.calledWith(ctx.err)).to.equal(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when there is a partial route match', function () {
|
||||
it('passes the csrf error on when the match is too short', function () {
|
||||
this.csrf.disableDefaultCsrfProtection('/foo', 'POST')
|
||||
this.csrf.middleware(this.req, this.res, this.next)
|
||||
return expect(this.next.calledWith(this.err)).to.equal(true)
|
||||
it('passes the csrf error on when the match is too short', function (ctx) {
|
||||
ctx.csrf.disableDefaultCsrfProtection('/foo', 'POST')
|
||||
ctx.csrf.middleware(ctx.req, ctx.res, ctx.next)
|
||||
return expect(ctx.next.calledWith(ctx.err)).to.equal(true)
|
||||
})
|
||||
|
||||
it('passes the csrf error on when the match is too long', function () {
|
||||
this.csrf.disableDefaultCsrfProtection('/foo/bar/baz', 'POST')
|
||||
this.csrf.middleware(this.req, this.res, this.next)
|
||||
return expect(this.next.calledWith(this.err)).to.equal(true)
|
||||
it('passes the csrf error on when the match is too long', function (ctx) {
|
||||
ctx.csrf.disableDefaultCsrfProtection('/foo/bar/baz', 'POST')
|
||||
ctx.csrf.middleware(ctx.req, ctx.res, ctx.next)
|
||||
return expect(ctx.next.calledWith(ctx.err)).to.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when there are multiple exclusions', function () {
|
||||
it('does not pass the csrf error on when the match is present', function () {
|
||||
this.csrf.disableDefaultCsrfProtection(this.path, 'POST')
|
||||
this.csrf.disableDefaultCsrfProtection('/test', 'POST')
|
||||
this.csrf.disableDefaultCsrfProtection('/a/b/c', 'POST')
|
||||
this.csrf.middleware(this.req, this.res, this.next)
|
||||
return expect(this.next.calledWith(this.err)).to.equal(false)
|
||||
it('does not pass the csrf error on when the match is present', function (ctx) {
|
||||
ctx.csrf.disableDefaultCsrfProtection(ctx.path, 'POST')
|
||||
ctx.csrf.disableDefaultCsrfProtection('/test', 'POST')
|
||||
ctx.csrf.disableDefaultCsrfProtection('/a/b/c', 'POST')
|
||||
ctx.csrf.middleware(ctx.req, ctx.res, ctx.next)
|
||||
return expect(ctx.next.calledWith(ctx.err)).to.equal(false)
|
||||
})
|
||||
|
||||
it('passes the csrf error on when the match is not present', function () {
|
||||
this.csrf.disableDefaultCsrfProtection('/url', 'POST')
|
||||
this.csrf.disableDefaultCsrfProtection('/test', 'POST')
|
||||
this.csrf.disableDefaultCsrfProtection('/a/b/c', 'POST')
|
||||
this.csrf.middleware(this.req, this.res, this.next)
|
||||
return expect(this.next.calledWith(this.err)).to.equal(true)
|
||||
it('passes the csrf error on when the match is not present', function (ctx) {
|
||||
ctx.csrf.disableDefaultCsrfProtection('/url', 'POST')
|
||||
ctx.csrf.disableDefaultCsrfProtection('/test', 'POST')
|
||||
ctx.csrf.disableDefaultCsrfProtection('/a/b/c', 'POST')
|
||||
ctx.csrf.middleware(ctx.req, ctx.res, ctx.next)
|
||||
return expect(ctx.next.calledWith(ctx.err)).to.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when the method does not match', function () {
|
||||
it('passes the csrf error on', function () {
|
||||
this.csrf.disableDefaultCsrfProtection(this.path, 'POST')
|
||||
this.req.method = 'GET'
|
||||
this.csrf.middleware(this.req, this.res, this.next)
|
||||
return expect(this.next.calledWith(this.err)).to.equal(true)
|
||||
it('passes the csrf error on', function (ctx) {
|
||||
ctx.csrf.disableDefaultCsrfProtection(ctx.path, 'POST')
|
||||
ctx.req.method = 'GET'
|
||||
ctx.csrf.middleware(ctx.req, ctx.res, ctx.next)
|
||||
return expect(ctx.next.calledWith(ctx.err)).to.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when the route is excluded, but the error is not a bad-csrf-token error', function () {
|
||||
it('passes the error on', function () {
|
||||
let err
|
||||
this.Csrf = SandboxedModule.require(modulePath, {
|
||||
globals: {
|
||||
console,
|
||||
},
|
||||
requires: {
|
||||
csurf: (this.csurf = sinon
|
||||
.stub()
|
||||
.returns(
|
||||
(this.csurf_csrf = sinon
|
||||
.stub()
|
||||
.callsArgWith(2, (err = { code: 'EOTHER' })))
|
||||
)),
|
||||
},
|
||||
})
|
||||
this.csrf = new this.Csrf()
|
||||
this.csrf.disableDefaultCsrfProtection(this.path, 'POST')
|
||||
this.csrf.middleware(this.req, this.res, this.next)
|
||||
expect(this.next.calledWith(err)).to.equal(true)
|
||||
return expect(this.next.calledWith(this.err)).to.equal(false)
|
||||
it('passes the error on', async function (ctx) {
|
||||
const err = { code: 'EOTHER' }
|
||||
|
||||
ctx.csurf_csrf.callsArgWith(2, err)
|
||||
|
||||
const csrf = new ctx.Csrf()
|
||||
csrf.disableDefaultCsrfProtection(ctx.path, 'POST')
|
||||
csrf.middleware(ctx.req, ctx.res, ctx.next)
|
||||
expect(ctx.next.calledWith(err)).to.equal(true)
|
||||
expect(ctx.next.calledWith(ctx.err)).to.equal(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('validateRequest', function () {
|
||||
describe('when the request is invalid', function () {
|
||||
it('calls the callback with error', function () {
|
||||
this.cb = sinon.stub()
|
||||
this.Csrf.validateRequest(this.req, this.cb)
|
||||
return expect(this.cb.calledWith(this.err)).to.equal(true)
|
||||
it('calls the callback with error', function (ctx) {
|
||||
ctx.cb = sinon.stub()
|
||||
ctx.Csrf.validateRequest(ctx.req, ctx.cb)
|
||||
expect(ctx.cb.calledWith(ctx.err)).to.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when the request is valid', function () {
|
||||
it('calls the callback without an error', function () {
|
||||
this.Csrf = SandboxedModule.require(modulePath, {
|
||||
globals: {
|
||||
console,
|
||||
},
|
||||
requires: {
|
||||
csurf: (this.csurf = sinon
|
||||
.stub()
|
||||
.returns((this.csurf_csrf = sinon.stub().callsArg(2)))),
|
||||
},
|
||||
})
|
||||
this.cb = sinon.stub()
|
||||
this.Csrf.validateRequest(this.req, this.cb)
|
||||
return expect(this.cb.calledWith()).to.equal(true)
|
||||
it('calls the callback without an error', async function (ctx) {
|
||||
vi.doMock('csurf', () => ({
|
||||
default: (ctx.csurf = sinon
|
||||
.stub()
|
||||
.returns((ctx.csurf_csrf = sinon.stub().callsArg(2)))),
|
||||
}))
|
||||
|
||||
ctx.Csrf = (await import(modulePath)).default
|
||||
ctx.cb = sinon.stub()
|
||||
ctx.Csrf.validateRequest(ctx.req, ctx.cb)
|
||||
expect(ctx.cb.calledWith()).to.equal(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('validateToken', function () {
|
||||
describe('when the request is invalid', function () {
|
||||
it('calls the callback with `false`', function () {
|
||||
this.cb = sinon.stub()
|
||||
this.Csrf.validateToken('token', {}, this.cb)
|
||||
expect(this.cb.calledWith(this.err)).to.equal(true)
|
||||
it('calls the callback with `false`', function (ctx) {
|
||||
ctx.cb = sinon.stub()
|
||||
ctx.Csrf.validateToken('token', {}, ctx.cb)
|
||||
expect(ctx.cb.calledWith(ctx.err)).to.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when the request is valid', function () {
|
||||
it('calls the callback with `true`', function () {
|
||||
this.Csrf = SandboxedModule.require(modulePath, {
|
||||
globals: {
|
||||
console,
|
||||
},
|
||||
requires: {
|
||||
csurf: (this.csurf = sinon
|
||||
.stub()
|
||||
.returns((this.csurf_csrf = sinon.stub().callsArg(2)))),
|
||||
},
|
||||
})
|
||||
this.cb = sinon.stub()
|
||||
this.Csrf.validateToken('goodtoken', {}, this.cb)
|
||||
return expect(this.cb.calledWith()).to.equal(true)
|
||||
it('calls the callback with `true`', async function (ctx) {
|
||||
vi.doMock('csurf', () => ({
|
||||
default: (ctx.csurf = sinon
|
||||
.stub()
|
||||
.returns((ctx.csurf_csrf = sinon.stub().callsArg(2)))),
|
||||
}))
|
||||
|
||||
ctx.Csrf = (await import(modulePath)).default
|
||||
ctx.cb = sinon.stub()
|
||||
ctx.Csrf.validateToken('goodtoken', {}, ctx.cb)
|
||||
expect(ctx.cb.calledWith()).to.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when there is no token', function () {
|
||||
it('calls the callback with an error', function () {
|
||||
this.Csrf = SandboxedModule.require(modulePath, {
|
||||
globals: {
|
||||
console,
|
||||
},
|
||||
requires: {
|
||||
csurf: (this.csurf = sinon
|
||||
.stub()
|
||||
.returns((this.csurf_csrf = sinon.stub().callsArg(2)))),
|
||||
},
|
||||
})
|
||||
this.cb = sinon.stub()
|
||||
this.Csrf.validateToken(null, {}, error => {
|
||||
it('calls the callback with an error', async function (ctx) {
|
||||
vi.doMock('csurf', () => ({
|
||||
default: (ctx.csurf = sinon
|
||||
.stub()
|
||||
.returns((ctx.csurf_csrf = sinon.stub().callsArg(2)))),
|
||||
}))
|
||||
|
||||
ctx.Csrf = (await import(modulePath)).default
|
||||
ctx.cb = sinon.stub()
|
||||
ctx.Csrf.validateToken(null, {}, error => {
|
||||
expect(error).to.exist
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
const assert = require('assert')
|
||||
const path = require('path')
|
||||
const sinon = require('sinon')
|
||||
const { expect } = require('chai')
|
||||
import { assert, describe, beforeEach, it, vi, expect } from 'vitest'
|
||||
import path from 'path'
|
||||
import sinon from 'sinon'
|
||||
|
||||
const modulePath = path.join(
|
||||
__dirname,
|
||||
import.meta.dirname,
|
||||
'../../../../app/src/infrastructure/GeoIpLookup'
|
||||
)
|
||||
|
||||
describe('GeoIpLookup', function () {
|
||||
beforeEach(function () {
|
||||
this.ipAddress = '12.34.56.78'
|
||||
beforeEach(async function (ctx) {
|
||||
ctx.ipAddress = '12.34.56.78'
|
||||
|
||||
this.stubbedResponse = {
|
||||
ip: this.ipAddress,
|
||||
ctx.stubbedResponse = {
|
||||
ip: ctx.ipAddress,
|
||||
country_code: 'GB',
|
||||
country_name: 'United Kingdom',
|
||||
region_code: 'H9',
|
||||
@@ -25,63 +24,67 @@ describe('GeoIpLookup', function () {
|
||||
metro_code: '',
|
||||
area_code: '',
|
||||
}
|
||||
this.fetchUtils = {
|
||||
fetchJson: sinon.stub().resolves(this.stubbedResponse),
|
||||
ctx.fetchUtils = {
|
||||
fetchJson: sinon.stub().resolves(ctx.stubbedResponse),
|
||||
}
|
||||
this.settings = {
|
||||
ctx.settings = {
|
||||
apis: {
|
||||
geoIpLookup: {
|
||||
url: 'http://lookup.com/',
|
||||
},
|
||||
},
|
||||
}
|
||||
this.GeoIpLookup = SandboxedModule.require(modulePath, {
|
||||
requires: {
|
||||
'@overleaf/fetch-utils': this.fetchUtils,
|
||||
'@overleaf/settings': this.settings,
|
||||
},
|
||||
})
|
||||
|
||||
vi.doMock('@overleaf/fetch-utils', () => ({
|
||||
...ctx.fetchUtils,
|
||||
}))
|
||||
|
||||
vi.doMock('@overleaf/settings', () => ({
|
||||
default: ctx.settings,
|
||||
}))
|
||||
|
||||
ctx.GeoIpLookup = (await import(modulePath)).default
|
||||
})
|
||||
|
||||
describe('isValidCurrencyParam', function () {
|
||||
it('should reject invalid currency codes', function () {
|
||||
expect(this.GeoIpLookup.isValidCurrencyParam('GBP')).to.equal(true)
|
||||
expect(this.GeoIpLookup.isValidCurrencyParam('USD')).to.equal(true)
|
||||
expect(this.GeoIpLookup.isValidCurrencyParam('AUD')).to.equal(true)
|
||||
expect(this.GeoIpLookup.isValidCurrencyParam('EUR')).to.equal(true)
|
||||
expect(this.GeoIpLookup.isValidCurrencyParam('SGD')).to.equal(true)
|
||||
expect(this.GeoIpLookup.isValidCurrencyParam('WAT')).to.equal(false)
|
||||
expect(this.GeoIpLookup.isValidCurrencyParam('NON')).to.equal(false)
|
||||
expect(this.GeoIpLookup.isValidCurrencyParam('LOL')).to.equal(false)
|
||||
it('should reject invalid currency codes', function (ctx) {
|
||||
expect(ctx.GeoIpLookup.isValidCurrencyParam('GBP')).to.equal(true)
|
||||
expect(ctx.GeoIpLookup.isValidCurrencyParam('USD')).to.equal(true)
|
||||
expect(ctx.GeoIpLookup.isValidCurrencyParam('AUD')).to.equal(true)
|
||||
expect(ctx.GeoIpLookup.isValidCurrencyParam('EUR')).to.equal(true)
|
||||
expect(ctx.GeoIpLookup.isValidCurrencyParam('SGD')).to.equal(true)
|
||||
expect(ctx.GeoIpLookup.isValidCurrencyParam('WAT')).to.equal(false)
|
||||
expect(ctx.GeoIpLookup.isValidCurrencyParam('NON')).to.equal(false)
|
||||
expect(ctx.GeoIpLookup.isValidCurrencyParam('LOL')).to.equal(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('getDetails', function () {
|
||||
beforeEach(function () {
|
||||
this.fetchUtils.fetchJson.resolves(this.stubbedResponse)
|
||||
beforeEach(function (ctx) {
|
||||
ctx.fetchUtils.fetchJson.resolves(ctx.stubbedResponse)
|
||||
})
|
||||
|
||||
describe('async', function () {
|
||||
it('should request the details using the ip', async function () {
|
||||
await this.GeoIpLookup.promises.getDetails(this.ipAddress)
|
||||
this.fetchUtils.fetchJson.should.have.been.calledWith(
|
||||
new URL(this.settings.apis.geoIpLookup.url + this.ipAddress)
|
||||
it('should request the details using the ip', async function (ctx) {
|
||||
await ctx.GeoIpLookup.promises.getDetails(ctx.ipAddress)
|
||||
ctx.fetchUtils.fetchJson.should.have.been.calledWith(
|
||||
new URL(ctx.settings.apis.geoIpLookup.url + ctx.ipAddress)
|
||||
)
|
||||
})
|
||||
|
||||
it('should return the ip details', async function () {
|
||||
const returnedDetails = await this.GeoIpLookup.promises.getDetails(
|
||||
this.ipAddress
|
||||
it('should return the ip details', async function (ctx) {
|
||||
const returnedDetails = await ctx.GeoIpLookup.promises.getDetails(
|
||||
ctx.ipAddress
|
||||
)
|
||||
assert.deepEqual(returnedDetails, this.stubbedResponse)
|
||||
assert.deepEqual(returnedDetails, ctx.stubbedResponse)
|
||||
})
|
||||
|
||||
it('should take the first ip in the string', async function () {
|
||||
await this.GeoIpLookup.promises.getDetails(
|
||||
` ${this.ipAddress} 123.123.123.123 234.234.234.234`
|
||||
it('should take the first ip in the string', async function (ctx) {
|
||||
await ctx.GeoIpLookup.promises.getDetails(
|
||||
` ${ctx.ipAddress} 123.123.123.123 234.234.234.234`
|
||||
)
|
||||
this.fetchUtils.fetchJson.should.have.been.calledWith(
|
||||
new URL(this.settings.apis.geoIpLookup.url + this.ipAddress)
|
||||
ctx.fetchUtils.fetchJson.should.have.been.calledWith(
|
||||
new URL(ctx.settings.apis.geoIpLookup.url + ctx.ipAddress)
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -89,57 +92,57 @@ describe('GeoIpLookup', function () {
|
||||
|
||||
describe('getCurrencyCode', function () {
|
||||
describe('async', function () {
|
||||
it('should return GBP for GB country', async function () {
|
||||
it('should return GBP for GB country', async function (ctx) {
|
||||
const { currencyCode, countryCode } =
|
||||
await this.GeoIpLookup.promises.getCurrencyCode(this.ipAddress)
|
||||
await ctx.GeoIpLookup.promises.getCurrencyCode(ctx.ipAddress)
|
||||
currencyCode.should.equal('GBP')
|
||||
countryCode.should.equal('GB')
|
||||
})
|
||||
|
||||
it('should return GBP for gb country', async function () {
|
||||
this.stubbedResponse.country_code = 'gb'
|
||||
it('should return GBP for gb country', async function (ctx) {
|
||||
ctx.stubbedResponse.country_code = 'gb'
|
||||
const { currencyCode, countryCode } =
|
||||
await this.GeoIpLookup.promises.getCurrencyCode(this.ipAddress)
|
||||
await ctx.GeoIpLookup.promises.getCurrencyCode(ctx.ipAddress)
|
||||
currencyCode.should.equal('GBP')
|
||||
countryCode.should.equal('GB')
|
||||
})
|
||||
|
||||
it('should return USD for US', async function () {
|
||||
this.stubbedResponse.country_code = 'US'
|
||||
it('should return USD for US', async function (ctx) {
|
||||
ctx.stubbedResponse.country_code = 'US'
|
||||
const { currencyCode, countryCode } =
|
||||
await this.GeoIpLookup.promises.getCurrencyCode(this.ipAddress)
|
||||
await ctx.GeoIpLookup.promises.getCurrencyCode(ctx.ipAddress)
|
||||
currencyCode.should.equal('USD')
|
||||
countryCode.should.equal('US')
|
||||
})
|
||||
|
||||
it('should return EUR for DE', async function () {
|
||||
this.stubbedResponse.country_code = 'DE'
|
||||
it('should return EUR for DE', async function (ctx) {
|
||||
ctx.stubbedResponse.country_code = 'DE'
|
||||
const { currencyCode, countryCode } =
|
||||
await this.GeoIpLookup.promises.getCurrencyCode(this.ipAddress)
|
||||
await ctx.GeoIpLookup.promises.getCurrencyCode(ctx.ipAddress)
|
||||
currencyCode.should.equal('EUR')
|
||||
countryCode.should.equal('DE')
|
||||
})
|
||||
|
||||
it('should default to USD if there is an error', async function () {
|
||||
this.fetchUtils.fetchJson.rejects(new Error('foo'))
|
||||
it('should default to USD if there is an error', async function (ctx) {
|
||||
ctx.fetchUtils.fetchJson.rejects(new Error('foo'))
|
||||
const { currencyCode, countryCode } =
|
||||
await this.GeoIpLookup.promises.getCurrencyCode(this.ipAddress)
|
||||
await ctx.GeoIpLookup.promises.getCurrencyCode(ctx.ipAddress)
|
||||
currencyCode.should.equal('USD')
|
||||
expect(countryCode).to.be.undefined
|
||||
})
|
||||
|
||||
it('should default to USD if there are no details', async function () {
|
||||
this.fetchUtils.fetchJson.resolves({})
|
||||
it('should default to USD if there are no details', async function (ctx) {
|
||||
ctx.fetchUtils.fetchJson.resolves({})
|
||||
const { currencyCode, countryCode } =
|
||||
await this.GeoIpLookup.promises.getCurrencyCode(this.ipAddress)
|
||||
await ctx.GeoIpLookup.promises.getCurrencyCode(ctx.ipAddress)
|
||||
currencyCode.should.equal('USD')
|
||||
expect(countryCode).to.be.undefined
|
||||
})
|
||||
|
||||
it('should default to USD if there is no match for their country', async function () {
|
||||
this.stubbedResponse.country_code = 'Non existant'
|
||||
it('should default to USD if there is no match for their country', async function (ctx) {
|
||||
ctx.stubbedResponse.country_code = 'Non existant'
|
||||
const { currencyCode, countryCode } =
|
||||
await this.GeoIpLookup.promises.getCurrencyCode(this.ipAddress)
|
||||
await ctx.GeoIpLookup.promises.getCurrencyCode(ctx.ipAddress)
|
||||
currencyCode.should.equal('USD')
|
||||
countryCode.should.equal('NON EXISTANT')
|
||||
})
|
||||
|
||||
@@ -1,31 +1,38 @@
|
||||
const { expect } = require('chai')
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
const MODULE_PATH = '../../../../app/src/infrastructure/Translations.js'
|
||||
const MODULE_PATH = '../../../../app/src/infrastructure/Translations.mjs'
|
||||
|
||||
describe('Translations', function () {
|
||||
let req, res, translations
|
||||
function runMiddlewares(cb) {
|
||||
translations.i18nMiddleware(req, res, () => {
|
||||
translations.setLangBasedOnDomainMiddleware(req, res, cb)
|
||||
})
|
||||
async function runMiddlewares(cb) {
|
||||
return await new Promise((resolve, reject) =>
|
||||
translations.i18nMiddleware(req, res, () => {
|
||||
translations.setLangBasedOnDomainMiddleware(req, res, (err, result) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
resolve(result)
|
||||
}
|
||||
})
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
translations = SandboxedModule.require(MODULE_PATH, {
|
||||
requires: {
|
||||
'@overleaf/settings': {
|
||||
i18n: {
|
||||
escapeHTMLInVars: false,
|
||||
subdomainLang: {
|
||||
www: { lngCode: 'en', url: 'https://www.overleaf.com' },
|
||||
fr: { lngCode: 'fr', url: 'https://fr.overleaf.com' },
|
||||
da: { lngCode: 'da', url: 'https://da.overleaf.com' },
|
||||
},
|
||||
beforeEach(async function () {
|
||||
vi.doMock('@overleaf/settings', () => ({
|
||||
default: {
|
||||
i18n: {
|
||||
escapeHTMLInVars: false,
|
||||
subdomainLang: {
|
||||
www: { lngCode: 'en', url: 'https://www.overleaf.com' },
|
||||
fr: { lngCode: 'fr', url: 'https://fr.overleaf.com' },
|
||||
da: { lngCode: 'da', url: 'https://da.overleaf.com' },
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}))
|
||||
|
||||
translations = (await import(MODULE_PATH)).default
|
||||
|
||||
req = {
|
||||
url: '/',
|
||||
@@ -41,8 +48,8 @@ describe('Translations', function () {
|
||||
})
|
||||
|
||||
describe('translate', function () {
|
||||
beforeEach(function (done) {
|
||||
runMiddlewares(done)
|
||||
beforeEach(async function () {
|
||||
await runMiddlewares()
|
||||
})
|
||||
|
||||
it('works', function () {
|
||||
@@ -55,8 +62,8 @@ describe('Translations', function () {
|
||||
})
|
||||
|
||||
describe('interpolation', function () {
|
||||
beforeEach(function (done) {
|
||||
runMiddlewares(done)
|
||||
beforeEach(async function () {
|
||||
await runMiddlewares()
|
||||
})
|
||||
|
||||
it('works', function () {
|
||||
@@ -94,34 +101,28 @@ describe('Translations', function () {
|
||||
})
|
||||
|
||||
describe('setLangBasedOnDomainMiddleware', function () {
|
||||
it('should set the lang to french if the domain is fr', function (done) {
|
||||
it('should set the lang to french if the domain is fr', async function () {
|
||||
req.headers.host = 'fr.overleaf.com'
|
||||
runMiddlewares(() => {
|
||||
expect(req.lng).to.equal('fr')
|
||||
done()
|
||||
})
|
||||
await runMiddlewares()
|
||||
expect(req.lng).to.equal('fr')
|
||||
})
|
||||
|
||||
describe('suggestedLanguageSubdomainConfig', function () {
|
||||
it('should set suggestedLanguageSubdomainConfig if the detected lang is different to subdomain lang', function (done) {
|
||||
it('should set suggestedLanguageSubdomainConfig if the detected lang is different to subdomain lang', async function () {
|
||||
req.headers['accept-language'] = 'da, en-gb;q=0.8, en;q=0.7'
|
||||
req.headers.host = 'fr.overleaf.com'
|
||||
runMiddlewares(() => {
|
||||
expect(res.locals.suggestedLanguageSubdomainConfig).to.exist
|
||||
expect(res.locals.suggestedLanguageSubdomainConfig.lngCode).to.equal(
|
||||
'da'
|
||||
)
|
||||
done()
|
||||
})
|
||||
await runMiddlewares()
|
||||
expect(res.locals.suggestedLanguageSubdomainConfig).to.exist
|
||||
expect(res.locals.suggestedLanguageSubdomainConfig.lngCode).to.equal(
|
||||
'da'
|
||||
)
|
||||
})
|
||||
|
||||
it('should not set suggestedLanguageSubdomainConfig if the detected lang is the same as subdomain lang', function (done) {
|
||||
it('should not set suggestedLanguageSubdomainConfig if the detected lang is the same as subdomain lang', async function () {
|
||||
req.headers['accept-language'] = 'da, en-gb;q=0.8, en;q=0.7'
|
||||
req.headers.host = 'da.overleaf.com'
|
||||
runMiddlewares(() => {
|
||||
expect(res.locals.suggestedLanguageSubdomainConfig).to.not.exist
|
||||
done()
|
||||
})
|
||||
await runMiddlewares()
|
||||
expect(res.locals.suggestedLanguageSubdomainConfig).to.not.exist
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user