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

cameri / nostream / 29897587965

22 Jul 2026 06:41AM UTC coverage: 69.694% (+1.0%) from 68.708%
29897587965

Pull #706

github

web-flow
Merge 1e37e3092 into 7c4b728c9
Pull Request #706: feat(admin): add settings editor tab to admin dashboard

2298 of 3662 branches covered (62.75%)

Branch coverage included in aggregate %.

165 of 181 new or added lines in 17 files covered. (91.16%)

3 existing lines in 2 files now uncovered.

5245 of 7161 relevant lines covered (73.24%)

28.67 hits per line

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

94.92
/src/controllers/admin/patch-settings-controller.ts
1
import { Request, Response } from 'express'
2

3
import { Settings } from '../../@types/settings'
4
import { IController } from '../../@types/controllers'
5
import { adminSettingsPatchBodySchema } from '../../schemas/admin-settings-schema'
2✔
6
import {
2✔
7
  appendSettingsAuditLog,
8
  getByPath,
9
  loadMergedSettings,
10
  loadUserSettings,
11
  saveSettings,
12
  setByPath,
13
  validatePathAgainstDefaults,
14
  validateSettings,
15
} from '../../utils/settings-config'
16
import {
2✔
17
  isWriteProtectedSettingsPath,
18
  redactSettingsValue,
19
} from '../../utils/settings-redaction'
20
import { validateSchema } from '../../utils/validation'
2✔
21

22
export class PatchAdminSettingsController implements IController {
2✔
23
  public async handleRequest(request: Request, response: Response): Promise<void> {
24
    const validation = validateSchema(adminSettingsPatchBodySchema)(request.body)
6✔
25
    if (validation.error) {
6✔
26
      response.status(400).setHeader('content-type', 'application/json').send({ error: 'Invalid request' })
1✔
27
      return
1✔
28
    }
29

30
    const changes = 'changes' in validation.value ? validation.value.changes : [validation.value]
5✔
31
    const issues = changes.flatMap(({ path }) => {
5✔
32
      if (isWriteProtectedSettingsPath(path)) {
6✔
33
        return [{ path, message: 'Path is write-protected' }]
1✔
34
      }
35

36
      return validatePathAgainstDefaults(path)
5✔
37
    })
38

39
    if (issues.length > 0) {
5✔
40
      response.status(400).setHeader('content-type', 'application/json').send({ error: 'Validation failed', issues })
2✔
41
      return
2✔
42
    }
43

44
    const userSettings = loadUserSettings() as unknown as Record<string, unknown>
3✔
45
    const nextUserSettings = changes.reduce(
3✔
46
      (settings, change) => setByPath(settings, change.path, change.value),
4✔
47
      userSettings,
48
    )
49
    const merged = loadMergedSettings() as unknown as Record<string, unknown>
3✔
50
    const mergedNext = changes.reduce(
3✔
51
      (settings, change) => setByPath(settings, change.path, getByPath(nextUserSettings, change.path)),
4✔
52
      merged,
53
    )
54
    const validationIssues = validateSettings(mergedNext as unknown as Settings)
3✔
55

56
    if (validationIssues.length > 0) {
3!
NEW
57
      response
×
58
        .status(400)
59
        .setHeader('content-type', 'application/json')
60
        .send({ error: 'Validation failed', issues: validationIssues })
NEW
61
      return
×
62
    }
63

64
    saveSettings(nextUserSettings as unknown as Settings)
3✔
65
    const updatedChanges = changes.map(({ path }) => ({
4✔
66
      path,
67
      value: redactSettingsValue(path, getByPath(nextUserSettings, path)),
68
      reload: getSettingsReloadBehavior(path),
69
    }))
70
    appendSettingsAuditLog({
3✔
71
      action: 'settings.updated',
72
      changes: updatedChanges.map(({ path, reload }) => ({ path, reload })),
4✔
73
      remoteAddress: request.ip,
74
    })
75

76
    if (changes.length === 1 && !('changes' in validation.value)) {
3✔
77
      const [change] = updatedChanges
2✔
78
      response.status(200).setHeader('content-type', 'application/json').send({ ok: true, ...change })
2✔
79
      return
2✔
80
    }
81

82
    response.status(200).setHeader('content-type', 'application/json').send({ ok: true, changes: updatedChanges })
1✔
83
  }
84
}
85

86
const getSettingsReloadBehavior = (path: string): 'hot-reload' | 'restart-required' => {
2✔
87
  if (path.startsWith('workers.') || path.startsWith('payments') || path.startsWith('network.')) {
4✔
88
    return 'restart-required'
3✔
89
  }
90

91
  return 'hot-reload'
1✔
92
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc