• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mozilla / blurts-server / 2419cf91-82da-42dd-aecd-24558d5ce7d9

pending completion
2419cf91-82da-42dd-aecd-24558d5ce7d9

push

circleci

GitHub
Merge pull request #2720 from mozilla/MNTOR-1053/fix-locale-singleton

278 of 1059 branches covered (26.25%)

Branch coverage included in aggregate %.

12 of 12 new or added lines in 4 files covered. (100.0%)

948 of 2881 relevant lines covered (32.91%)

5.21 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/app.js
1
import express from 'express'
2
import session from 'express-session'
3
import connectRedis from 'connect-redis'
4
import helmet from 'helmet'
5
import accepts from 'accepts'
6
import redis from 'redis'
7

8
import AppConstants from './app-constants.js'
9
import { localStorage } from './utils/local-storage.js'
10
import { errorHandler } from './middleware/error.js'
11
import { initFluentBundles, updateLocale } from './utils/fluent.js'
12
import { loadBreachesIntoApp } from './utils/hibp.js'
13
import indexRouter from './routes/index.js'
14

15
const app = express()
×
16
const isDev = AppConstants.NODE_ENV === 'dev'
×
17

18
// Determine from where to serve client code/assets:
19
// Build script is triggered for `npm start` and assets are served from /dist.
20
// Build script is NOT run for `npm run dev`, assets are served from /src, and nodemon restarts server without build (faster dev).
21
const staticPath = process.env.npm_lifecycle_event === 'start' ? '../dist' : './client'
×
22

23
await initFluentBundles()
×
24

25
async function getRedisStore () {
26
  const RedisStoreConstructor = connectRedis(session)
×
27
  if (['', 'redis-mock'].includes(AppConstants.REDIS_URL)) {
×
28
    const redisMock = await import('redis-mock') // for devs without local redis
×
29
    return new RedisStoreConstructor({ client: redisMock.default.createClient() })
×
30
  }
31
  return new RedisStoreConstructor({ client: redis.createClient({ url: AppConstants.REDIS_URL }) })
×
32
}
33

34
// middleware
35
app.use(helmet())
×
36

37
// disable forced https to allow localhost on Safari
38
app.use(
×
39
  helmet.contentSecurityPolicy({
40
    directives: {
41
      upgradeInsecureRequests: isDev ? null : []
×
42
    }
43
  })
44
)
45

46
// fallback to default 'no-referrer' only when 'strict-origin-when-cross-origin' not available
47
app.use(
×
48
  helmet.referrerPolicy({
49
    policy: ['no-referrer', 'strict-origin-when-cross-origin']
50
  })
51
)
52

53
// When a text/html request is received, negotiate and store the requested language
54
// Using asyncLocalStorage avoids having to pass req context down through every function (e.g. getMessage())
55
app.use((req, res, next) => {
×
56
  if (!req.headers.accept?.startsWith('text/html')) return next()
×
57

58
  localStorage.run(new Map(), () => {
×
59
    req.locale = updateLocale(accepts(req).languages())
×
60
    localStorage.getStore().set('locale', req.locale)
×
61
    next()
×
62
  })
63
})
64

65
// MNTOR-1009:
66
// Because of heroku's proxy settings, request / cookies are not persisted between calls
67
// Setting the trust proxy to high and securing the cookie allowed the cookie to persist
68
// If cookie.secure is set as true, for nodejs behind proxy, "trust proxy" needs to be set
69
if (AppConstants.NODE_ENV === 'heroku') {
×
70
  app.set('trust proxy', 1)
×
71
}
72

73
// session
74
const SESSION_DURATION_HOURS = AppConstants.SESSION_DURATION_HOURS || 48
×
75
app.use(session({
×
76
  cookie: {
77
    maxAge: SESSION_DURATION_HOURS * 60 * 60 * 1000, // 48 hours
78
    rolling: true,
79
    sameSite: 'lax',
80
    secure: !isDev
81
  },
82
  resave: false,
83
  saveUninitialized: true,
84
  secret: AppConstants.COOKIE_SECRET,
85
  store: await getRedisStore()
86
}))
87

88
// Load breaches into namespaced cache
89
try {
×
90
  await loadBreachesIntoApp(app)
×
91
} catch (error) {
92
  console.error('Error loading breaches into app.locals', error)
×
93
}
94

95
// routing
96
app.use(express.static(staticPath))
×
97
app.use('/', indexRouter)
×
98
app.use(express.json())
×
99
app.use(errorHandler)
×
100

101
// start server
102
app.listen(AppConstants.PORT, function () {
×
103
  console.log(`MONITOR V2: Server listening at ${this.address().port}`)
×
104
  console.log(`Static files served from ${staticPath}`)
×
105
})
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc