diff options
Diffstat (limited to 'frontend')
21 files changed, 111 insertions, 260 deletions
diff --git a/frontend/src/backend.js b/frontend/src/backend.js index cc8604a..dc5089f 100644 --- a/frontend/src/backend.js +++ b/frontend/src/backend.js @@ -17,7 +17,7 @@ async function json(method, url, data, json, headers) { const options = { - method: method, + method, body: json != null ? JSON.stringify(json) : data, mode: "cors", cache: "no-cache", diff --git a/frontend/src/components/Notifications.js b/frontend/src/components/Notifications.js index 92731d9..a81eba1 100644 --- a/frontend/src/components/Notifications.js +++ b/frontend/src/components/Notifications.js @@ -67,7 +67,7 @@ class Notifications extends Component { n.variant = "blue"; return this.pushNotification(n); default: - return; + return null; } }; diff --git a/frontend/src/components/Notifications.scss b/frontend/src/components/Notifications.scss index 98d228e..5852c7d 100644 --- a/frontend/src/components/Notifications.scss +++ b/frontend/src/components/Notifications.scss @@ -32,7 +32,7 @@ } &.notification-open { - transform: translateX(0px); + transform: translateX(0); } &.notification-closed { diff --git a/frontend/src/components/fields/ChoiceField.scss b/frontend/src/components/fields/ChoiceField.scss index c8c7ff1..85986af 100644 --- a/frontend/src/components/fields/ChoiceField.scss +++ b/frontend/src/components/fields/ChoiceField.scss @@ -27,8 +27,8 @@ } .field-options { - position: absolute; - z-index: 20; + position: static; + z-index: 100; top: 35px; display: none; width: 100%; diff --git a/frontend/src/components/fields/InputField.scss b/frontend/src/components/fields/InputField.scss index e8ef46a..eafb2ab 100644 --- a/frontend/src/components/fields/InputField.scss +++ b/frontend/src/components/fields/InputField.scss @@ -47,15 +47,15 @@ background-color: $color-primary-4 !important; } + .file-label::after { + background-color: $color-secondary-4 !important; + } + .field-value input, .field-value .file-label { color: $color-primary-3 !important; background-color: $color-primary-4 !important; } - - .file-label::after { - background-color: $color-secondary-4 !important; - } } &.field-invalid { diff --git a/frontend/src/components/fields/TagField.scss b/frontend/src/components/fields/TagField.scss index 737f11f..723e71f 100644 --- a/frontend/src/components/fields/TagField.scss +++ b/frontend/src/components/fields/TagField.scss @@ -10,6 +10,18 @@ } } + .react-tags { + position: relative; + display: flex; + border-radius: 4px; + background-color: $color-primary-2; + + &:focus-within, + &:focus-within .react-tags__search-input { + background-color: $color-primary-1; + } + } + &.field-small { font-size: 0.8em; } @@ -39,18 +51,6 @@ } } - .react-tags { - position: relative; - display: flex; - border-radius: 4px; - background-color: $color-primary-2; - - &:focus-within, - &:focus-within .react-tags__search-input { - background-color: $color-primary-1; - } - } - .react-tags__selected { display: inline-block; flex: 0 1; @@ -154,4 +154,4 @@ cursor: auto; opacity: 0.5; } -}
\ No newline at end of file +} diff --git a/frontend/src/components/filters/AdvancedFilters.js b/frontend/src/components/filters/AdvancedFilters.js index 2b479ed..15667a5 100644 --- a/frontend/src/components/filters/AdvancedFilters.js +++ b/frontend/src/components/filters/AdvancedFilters.js @@ -33,7 +33,7 @@ class AdvancedFilters extends Component { const active = ["client_address", "client_port", "min_duration", "max_duration", "min_bytes", "max_bytes"] .some(f => this.urlParams.has(f)); if (this.state.active !== active) { - this.setState({active: active}); + this.setState({active}); } }; dispatcher.register("connections_filters", this.connectionsFiltersCallback); diff --git a/frontend/src/components/filters/RulesConnectionsFilter.js b/frontend/src/components/filters/RulesConnectionsFilter.js index 86eae7e..37d36b7 100644 --- a/frontend/src/components/filters/RulesConnectionsFilter.js +++ b/frontend/src/components/filters/RulesConnectionsFilter.js @@ -35,17 +35,17 @@ class RulesConnectionsFilter extends Component { const params = new URLSearchParams(this.props.location.search); let activeRules = params.getAll("matched_rules") || []; - backend.get("/api/rules").then(res => { - let rules = res.json.flatMap(rule => rule.enabled ? [{id: rule.id, name: rule.name}] : []); - activeRules = rules.filter(rule => activeRules.some(id => rule.id === id)); + backend.get("/api/rules").then((res) => { + let rules = res.json.flatMap((rule) => rule.enabled ? [{id: rule.id, name: rule.name}] : []); + activeRules = rules.filter((rule) => activeRules.some(id => rule.id === id)); this.setState({rules, activeRules}); }); - this.connectionsFiltersCallback = payload => { + this.connectionsFiltersCallback = (payload) => { if ("matched_rules" in payload && !_.isEqual(payload["matched_rules"].sort(), this.state.activeRules.sort())) { - const newRules = this.state.rules.filter(r => payload["matched_rules"].includes(r.id)); + const newRules = this.state.rules.filter((r) => payload["matched_rules"].includes(r.id)); this.setState({ - activeRules: newRules.map(r => { + activeRules: newRules.map((r) => { return {id: r.id, name: r.name}; }) }); @@ -61,7 +61,7 @@ class RulesConnectionsFilter extends Component { onChange = (activeRules) => { if (!_.isEqual(activeRules.sort(), this.state.activeRules.sort())) { this.setState({activeRules}); - dispatcher.dispatch("connections_filters", {"matched_rules": activeRules.map(r => r.id)}); + dispatcher.dispatch("connections_filters", {"matched_rules": activeRules.map((r) => r.id)}); } }; diff --git a/frontend/src/components/filters/StringConnectionsFilter.js b/frontend/src/components/filters/StringConnectionsFilter.js index c3c5925..c5d7075 100644 --- a/frontend/src/components/filters/StringConnectionsFilter.js +++ b/frontend/src/components/filters/StringConnectionsFilter.js @@ -33,7 +33,7 @@ class StringConnectionsFilter extends Component { let params = new URLSearchParams(this.props.location.search); this.updateStateFromFilterValue(params.get(this.props.filterName)); - this.connectionsFiltersCallback = payload => { + this.connectionsFiltersCallback = (payload) => { const name = this.props.filterName; if (name in payload && this.state.filterValue !== payload[name]) { this.updateStateFromFilterValue(payload[name]); @@ -56,7 +56,7 @@ class StringConnectionsFilter extends Component { fieldValue = this.props.replaceFunc(fieldValue); } if (this.isValueValid(fieldValue)) { - this.setState({fieldValue, filterValue: filterValue}); + this.setState({fieldValue, filterValue}); } else { this.setState({fieldValue, invalidValue: true}); } @@ -98,9 +98,9 @@ class StringConnectionsFilter extends Component { } this.setState({ - fieldValue: fieldValue, + fieldValue, timeoutHandle: setTimeout(() => { - this.setState({filterValue: filterValue}); + this.setState({filterValue}); this.changeFilterValue(filterValue); }, 500), invalidValue: false diff --git a/frontend/src/components/pages/ConfigurationPage.js b/frontend/src/components/pages/ConfigurationPage.js index 2bd2da7..4f0ce21 100644 --- a/frontend/src/components/pages/ConfigurationPage.js +++ b/frontend/src/components/pages/ConfigurationPage.js @@ -59,11 +59,11 @@ class ConfigurationPage extends Component { validateSettings = (settings) => { let valid = true; - if (!validation.isValidAddress(settings.config.server_address, true)) { + if (!validation.isValidAddress(settings.config["server_address"], true)) { this.setState({serverAddressError: "invalid ip_address"}); valid = false; } - if (settings.config.flag_regex.length < 8) { + if (settings.config["flag_regex"].length < 8) { this.setState({flagRegexError: "flag_regex.length < 8"}); valid = false; } @@ -84,7 +84,7 @@ class ConfigurationPage extends Component { this.setState({ newUsername: "", newPassword: "", - settings: settings + settings }); } else { this.setState({ @@ -128,15 +128,15 @@ class ConfigurationPage extends Component { <Container className="p-0"> <Row> <Col> - <InputField name="server_address" value={settings.config.server_address} + <InputField name="server_address" value={settings.config["server_address"]} error={this.state.serverAddressError} - onChange={(v) => this.updateParam((s) => s.config.server_address = v)}/> - <InputField name="flag_regex" value={settings.config.flag_regex} - onChange={(v) => this.updateParam((s) => s.config.flag_regex = v)} + onChange={(v) => this.updateParam((s) => s.config["server_address"] = v)}/> + <InputField name="flag_regex" value={settings.config["flag_regex"]} + onChange={(v) => this.updateParam((s) => s.config["flag_regex"] = v)} error={this.state.flagRegexError}/> <div style={{"marginTop": "10px"}}> - <CheckField checked={settings.config.auth_required} name="auth_required" - onChange={(v) => this.updateParam((s) => s.config.auth_required = v)}/> + <CheckField checked={settings.config["auth_required"]} name="auth_required" + onChange={(v) => this.updateParam((s) => s.config["auth_required"] = v)}/> </div> </Col> diff --git a/frontend/src/components/panels/ConnectionsPane.js b/frontend/src/components/panels/ConnectionsPane.js index 23c6114..9418fad 100644 --- a/frontend/src/components/panels/ConnectionsPane.js +++ b/frontend/src/components/panels/ConnectionsPane.js @@ -178,7 +178,7 @@ class ConnectionsPane extends Component { let firstConnection = this.state.firstConnection; let lastConnection = this.state.lastConnection; - if (additionalParams !== undefined && additionalParams.from !== undefined && additionalParams.to === undefined) { + if (additionalParams && additionalParams.from && !additionalParams.to) { if (res.length > 0) { if (!isInitial) { res = res.slice(1); @@ -194,7 +194,7 @@ class ConnectionsPane extends Component { firstConnection = connections[0]; } } - } else if (additionalParams !== undefined && additionalParams.to !== undefined && additionalParams.from === undefined) { + } else if (additionalParams && additionalParams.to && !additionalParams.from) { if (res.length > 0) { connections = res.slice(0, res.length - 1).concat(this.state.connections); firstConnection = connections[0]; @@ -215,12 +215,7 @@ class ConnectionsPane extends Component { } } - this.setState({ - loading: false, - connections: connections, - firstConnection: firstConnection, - lastConnection: lastConnection - }); + this.setState({loading: false, connections, firstConnection, lastConnection}); if (firstConnection != null && lastConnection != null) { dispatcher.dispatch("connection_updates", { diff --git a/frontend/src/components/panels/PcapsPane.js b/frontend/src/components/panels/PcapsPane.js index 64e7804..ddc5948 100644 --- a/frontend/src/components/panels/PcapsPane.js +++ b/frontend/src/components/panels/PcapsPane.js @@ -181,15 +181,15 @@ class PcapsPane extends Component { }; const uploadCurlCommand = createCurlCommand("/pcap/upload", "POST", null, { - file: "@" + ((this.state.uploadSelectedFile != null && this.state.isUploadFileValid) ? + "file": "@" + ((this.state.uploadSelectedFile != null && this.state.isUploadFileValid) ? this.state.uploadSelectedFile.name : "invalid.pcap"), - flush_all: this.state.uploadFlushAll + "flush_all": this.state.uploadFlushAll }); const fileCurlCommand = createCurlCommand("/pcap/file", "POST", { - file: this.state.fileValue, - flush_all: this.state.processFlushAll, - delete_original_file: this.state.deleteOriginalFile + "file": this.state.fileValue, + "flush_all": this.state.processFlushAll, + "delete_original_file": this.state.deleteOriginalFile }); return ( diff --git a/frontend/src/components/panels/RulesPane.js b/frontend/src/components/panels/RulesPane.js index d872b47..0bbd407 100644 --- a/frontend/src/components/panels/RulesPane.js +++ b/frontend/src/components/panels/RulesPane.js @@ -142,23 +142,23 @@ class RulesPane extends Component { this.setState({ruleColorError: "color is not hexcolor"}); valid = false; } - if (!validation.isValidPort(rule.filter.service_port)) { + if (!validation.isValidPort(rule.filter["service_port"])) { this.setState({ruleServicePortError: "service_port > 65565"}); valid = false; } - if (!validation.isValidPort(rule.filter.client_port)) { + if (!validation.isValidPort(rule.filter["client_port"])) { this.setState({ruleClientPortError: "client_port > 65565"}); valid = false; } - if (!validation.isValidAddress(rule.filter.client_address)) { + if (!validation.isValidAddress(rule.filter["client_address"])) { this.setState({ruleClientAddressError: "client_address is not ip_address"}); valid = false; } - if (rule.filter.min_duration > rule.filter.max_duration) { + if (rule.filter["min_duration"] > rule.filter["max_duration"]) { this.setState({ruleDurationError: "min_duration > max_dur."}); valid = false; } - if (rule.filter.min_bytes > rule.filter.max_bytes) { + if (rule.filter["min_bytes"] > rule.filter["max_bytes"]) { this.setState({ruleBytesError: "min_bytes > max_bytes"}); valid = false; } @@ -175,9 +175,9 @@ class RulesPane extends Component { const newPattern = _.cloneDeep(this.emptyPattern); this.setState({ selectedRule: null, - newRule: newRule, + newRule, selectedPattern: null, - newPattern: newPattern, + newPattern, patternRegexFocused: false, patternOccurrencesFocused: false, ruleNameError: null, @@ -210,9 +210,7 @@ class RulesPane extends Component { const newPattern = _.cloneDeep(this.emptyPattern); this.currentRule().patterns.push(pattern); - this.setState({ - newPattern: newPattern - }); + this.setState({newPattern}); }; editPattern = (pattern) => { @@ -237,7 +235,7 @@ class RulesPane extends Component { valid = false; this.setState({patternRegexFocused: true}); } - if (pattern.min_occurrences > pattern.max_occurrences) { + if (pattern["min_occurrences"] > pattern["max_occurrences"]) { valid = false; this.setState({patternOccurrencesFocused: true}); } @@ -273,25 +271,25 @@ class RulesPane extends Component { this.setState({patternRegexFocused: pattern.regex === ""}); }}/> </td> - <td><CheckField small checked={pattern.flags.caseless} - onChange={(v) => this.updateParam(() => pattern.flags.caseless = v)}/></td> - <td><CheckField small checked={pattern.flags.dot_all} - onChange={(v) => this.updateParam(() => pattern.flags.dot_all = v)}/></td> - <td><CheckField small checked={pattern.flags.multi_line} - onChange={(v) => this.updateParam(() => pattern.flags.multi_line = v)}/></td> - <td><CheckField small checked={pattern.flags.utf_8_mode} - onChange={(v) => this.updateParam(() => pattern.flags.utf_8_mode = v)}/></td> - <td><CheckField small checked={pattern.flags.unicode_property} - onChange={(v) => this.updateParam(() => pattern.flags.unicode_property = v)}/></td> + <td><CheckField small checked={pattern.flags["caseless"]} + onChange={(v) => this.updateParam(() => pattern.flags["caseless"] = v)}/></td> + <td><CheckField small checked={pattern.flags["dot_all"]} + onChange={(v) => this.updateParam(() => pattern.flags["dot_all"] = v)}/></td> + <td><CheckField small checked={pattern.flags["multi_line"]} + onChange={(v) => this.updateParam(() => pattern.flags["multi_line"] = v)}/></td> + <td><CheckField small checked={pattern.flags["utf_8_mode"]} + onChange={(v) => this.updateParam(() => pattern.flags["utf_8_mode"] = v)}/></td> + <td><CheckField small checked={pattern.flags["unicode_property"]} + onChange={(v) => this.updateParam(() => pattern.flags["unicode_property"] = v)}/></td> <td style={{"width": "70px"}}> - <NumericField small value={pattern.min_occurrences} + <NumericField small value={pattern["min_occurrences"]} active={this.state.patternOccurrencesFocused} - onChange={(v) => this.updateParam(() => pattern.min_occurrences = v)}/> + onChange={(v) => this.updateParam(() => pattern["min_occurrences"] = v)}/> </td> <td style={{"width": "70px"}}> - <NumericField small value={pattern.max_occurrences} + <NumericField small value={pattern["max_occurrences"]} active={this.state.patternOccurrencesFocused} - onChange={(v) => this.updateParam(() => pattern.max_occurrences = v)}/> + onChange={(v) => this.updateParam(() => pattern["max_occurrences"] = v)}/> </td> <td><ChoiceField inline small keys={[0, 1, 2]} values={["both", "c->s", "s->c"]} value={this.directions[pattern.direction]} @@ -305,13 +303,13 @@ class RulesPane extends Component { : <tr key={"new_pattern"} className="row-small"> <td>{p.regex}</td> - <td>{p.flags.caseless ? "yes" : "no"}</td> - <td>{p.flags.dot_all ? "yes" : "no"}</td> - <td>{p.flags.multi_line ? "yes" : "no"}</td> - <td>{p.flags.utf_8_mode ? "yes" : "no"}</td> - <td>{p.flags.unicode_property ? "yes" : "no"}</td> - <td>{p.min_occurrences}</td> - <td>{p.max_occurrences}</td> + <td>{p.flags["caseless"] ? "yes" : "no"}</td> + <td>{p.flags["dot_all"] ? "yes" : "no"}</td> + <td>{p.flags["multi_line"] ? "yes" : "no"}</td> + <td>{p.flags["utf_8_mode"] ? "yes" : "no"}</td> + <td>{p.flags["unicode_property"] ? "yes" : "no"}</td> + <td>{p["min_occurrences"]}</td> + <td>{p["max_occurrences"]}</td> <td>{this.directions[p.direction]}</td> {!isUpdate && <td><ButtonField variant="blue" small rounded name="edit" onClick={() => this.editPattern(p)}/></td>} @@ -373,32 +371,32 @@ class RulesPane extends Component { <Col style={{"paddingTop": "6px"}}> <span>filters:</span> - <NumericField name="service_port" inline value={rule.filter.service_port} - onChange={(v) => this.updateParam((r) => r.filter.service_port = v)} + <NumericField name="service_port" inline value={rule.filter["service_port"]} + onChange={(v) => this.updateParam((r) => r.filter["service_port"] = v)} min={0} max={65565} error={this.state.ruleServicePortError} readonly={isUpdate}/> - <NumericField name="client_port" inline value={rule.filter.client_port} - onChange={(v) => this.updateParam((r) => r.filter.client_port = v)} + <NumericField name="client_port" inline value={rule.filter["client_port"]} + onChange={(v) => this.updateParam((r) => r.filter["client_port"] = v)} min={0} max={65565} error={this.state.ruleClientPortError} readonly={isUpdate}/> - <InputField name="client_address" value={rule.filter.client_address} + <InputField name="client_address" value={rule.filter["client_address"]} error={this.state.ruleClientAddressError} readonly={isUpdate} - onChange={(v) => this.updateParam((r) => r.filter.client_address = v)}/> + onChange={(v) => this.updateParam((r) => r.filter["client_address"] = v)}/> </Col> <Col style={{"paddingTop": "11px"}}> - <NumericField name="min_duration" inline value={rule.filter.min_duration} + <NumericField name="min_duration" inline value={rule.filter["min_duration"]} error={this.state.ruleDurationError} readonly={isUpdate} - onChange={(v) => this.updateParam((r) => r.filter.min_duration = v)}/> - <NumericField name="max_duration" inline value={rule.filter.max_duration} + onChange={(v) => this.updateParam((r) => r.filter["min_duration"] = v)}/> + <NumericField name="max_duration" inline value={rule.filter["max_duration"]} error={this.state.ruleDurationError} readonly={isUpdate} - onChange={(v) => this.updateParam((r) => r.filter.max_duration = v)}/> - <NumericField name="min_bytes" inline value={rule.filter.min_bytes} + onChange={(v) => this.updateParam((r) => r.filter["max_duration"] = v)}/> + <NumericField name="min_bytes" inline value={rule.filter["min_bytes"]} error={this.state.ruleBytesError} readonly={isUpdate} - onChange={(v) => this.updateParam((r) => r.filter.min_bytes = v)}/> - <NumericField name="max_bytes" inline value={rule.filter.max_bytes} + onChange={(v) => this.updateParam((r) => r.filter["min_bytes"] = v)}/> + <NumericField name="max_bytes" inline value={rule.filter["max_bytes"]} error={this.state.ruleBytesError} readonly={isUpdate} - onChange={(v) => this.updateParam((r) => r.filter.max_bytes = v)}/> + onChange={(v) => this.updateParam((r) => r.filter["max_bytes"] = v)}/> </Col> </Row> </Container> diff --git a/frontend/src/components/panels/SearchPane.js b/frontend/src/components/panels/SearchPane.js index d36e85e..776ebd0 100644 --- a/frontend/src/components/panels/SearchPane.js +++ b/frontend/src/components/panels/SearchPane.js @@ -70,20 +70,20 @@ class SearchPane extends Component { loadSearches = () => { backend.get("/api/searches") - .then(res => this.setState({searches: res.json, searchesStatusCode: res.status})) - .catch(res => this.setState({searchesStatusCode: res.status, searchesResponse: JSON.stringify(res.json)})); + .then((res) => this.setState({searches: res.json, searchesStatusCode: res.status})) + .catch((res) => this.setState({searchesStatusCode: res.status, searchesResponse: JSON.stringify(res.json)})); }; performSearch = () => { const options = this.state.currentSearchOptions; this.setState({loading: true}); if (this.validateSearch(options)) { - backend.post("/api/searches/perform", options).then(res => { + backend.post("/api/searches/perform", options).then((res) => { this.reset(); this.setState({searchStatusCode: res.status, loading: false}); this.loadSearches(); this.viewSearch(res.json.id); - }).catch(res => { + }).catch((res) => { this.setState({ searchStatusCode: res.status, searchResponse: JSON.stringify(res.json), loading: false @@ -168,7 +168,7 @@ class SearchPane extends Component { render() { const options = this.state.currentSearchOptions; - let searches = this.state.searches.map(s => + let searches = this.state.searches.map((s) => <tr key={s.id} className="row-small row-clickable"> <td>{s.id.substring(0, 8)}</td> <td>{this.extractPattern(s["search_options"])}</td> diff --git a/frontend/src/components/panels/StreamsPane.js b/frontend/src/components/panels/StreamsPane.js index 41ab33d..1819fec 100644 --- a/frontend/src/components/panels/StreamsPane.js +++ b/frontend/src/components/panels/StreamsPane.js @@ -18,7 +18,7 @@ import DOMPurify from "dompurify"; import React, {Component} from "react"; import {Row} from "react-bootstrap"; -import ReactJson from "react-json-view" +import ReactJson from "react-json-view"; import backend from "../../backend"; import log from "../../log"; import {downloadBlob, getHeaderValue} from "../../utils"; @@ -72,7 +72,7 @@ class StreamsPane extends Component { setFormat = (format) => { if (this.validFormats.includes(format)) { - this.setState({format: format}); + this.setState({format}); } }; diff --git a/frontend/src/index.js b/frontend/src/index.js index d00df88..62cb974 100644 --- a/frontend/src/index.js +++ b/frontend/src/index.js @@ -21,7 +21,6 @@ import ReactDOM from "react-dom"; import App from "./components/App"; import "./index.scss"; import notifications from "./notifications"; -import * as serviceWorker from "./serviceWorker"; notifications.createWebsocket(); @@ -31,5 +30,3 @@ ReactDOM.render( // </React.StrictMode>, document.getElementById("root") ); - -serviceWorker.unregister(); diff --git a/frontend/src/index.scss b/frontend/src/index.scss index ea360be..1378d81 100644 --- a/frontend/src/index.scss +++ b/frontend/src/index.scss @@ -82,9 +82,11 @@ a { 0% { opacity: 1; } + 50% { opacity: 0.3; } + 100% { opacity: 1; } diff --git a/frontend/src/log.js b/frontend/src/log.js index 424e1b4..0afac47 100644 --- a/frontend/src/log.js +++ b/frontend/src/log.js @@ -16,9 +16,14 @@ */ const log = { - debug: (...obj) => console.info(...obj), + debug: (...obj) => isDevelopment() && console.info(...obj), info: (...obj) => console.info(...obj), + warn: (...obj) => console.warn(...obj), error: (...obj) => console.error(obj) }; +function isDevelopment() { + return !process.env.NODE_ENV || process.env.NODE_ENV === 'development'; +} + export default log; diff --git a/frontend/src/serviceWorker.js b/frontend/src/serviceWorker.js deleted file mode 100644 index a1f0ba8..0000000 --- a/frontend/src/serviceWorker.js +++ /dev/null @@ -1,141 +0,0 @@ -// This optional code is used to register a service worker. -// register() is not called by default. - -// This lets the app load faster on subsequent visits in production, and gives -// it offline capabilities. However, it also means that developers (and users) -// will only see deployed updates on subsequent visits to a page, after all the -// existing tabs open on the page have been closed, since previously cached -// resources are updated in the background. - -// To learn more about the benefits of this model and instructions on how to -// opt-in, read https://bit.ly/CRA-PWA - -const isLocalhost = Boolean( - window.location.hostname === "localhost" || - // [::1] is the IPv6 localhost address. - window.location.hostname === "[::1]" || - // 127.0.0.0/8 are considered localhost for IPv4. - window.location.hostname.match( - /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ - ) -); - -export function register(config) { - if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) { - // The URL constructor is available in all browsers that support SW. - const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); - if (publicUrl.origin !== window.location.origin) { - // Our service worker won't work if PUBLIC_URL is on a different origin - // from what our page is served on. This might happen if a CDN is used to - // serve assets; see https://github.com/facebook/create-react-app/issues/2374 - return; - } - - window.addEventListener("load", () => { - const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; - - if (isLocalhost) { - // This is running on localhost. Let's check if a service worker still exists or not. - checkValidServiceWorker(swUrl, config); - - // Add some additional logging to localhost, pointing developers to the - // service worker/PWA documentation. - navigator.serviceWorker.ready.then(() => { - console.log( - "This web app is being served cache-first by a service " + - "worker. To learn more, visit https://bit.ly/CRA-PWA" - ); - }); - } else { - // Is not localhost. Just register service worker - registerValidSW(swUrl, config); - } - }); - } -} - -function registerValidSW(swUrl, config) { - navigator.serviceWorker - .register(swUrl) - .then((registration) => { - registration.onupdatefound = () => { - const installingWorker = registration.installing; - if (installingWorker == null) { - return; - } - installingWorker.onstatechange = () => { - if (installingWorker.state === "installed") { - if (navigator.serviceWorker.controller) { - // At this point, the updated precached content has been fetched, - // but the previous service worker will still serve the older - // content until all client tabs are closed. - console.log( - "New content is available and will be used when all " + - "tabs for this page are closed. See https://bit.ly/CRA-PWA." - ); - - // Execute callback - if (config && config.onUpdate) { - config.onUpdate(registration); - } - } else { - // At this point, everything has been precached. - // It's the perfect time to display a - // "Content is cached for offline use." message. - console.log("Content is cached for offline use."); - - // Execute callback - if (config && config.onSuccess) { - config.onSuccess(registration); - } - } - } - }; - }; - }) - .catch((error) => { - console.error("Error during service worker registration:", error); - }); -} - -function checkValidServiceWorker(swUrl, config) { - // Check if the service worker can be found. If it can't reload the page. - fetch(swUrl, { - headers: {"Service-Worker": "script"}, - }) - .then((response) => { - // Ensure service worker exists, and that we really are getting a JS file. - const contentType = response.headers.get("content-type"); - if ( - response.status === 404 || - (contentType != null && contentType.indexOf("javascript") === -1) - ) { - // No service worker found. Probably a different app. Reload the page. - navigator.serviceWorker.ready.then((registration) => { - registration.unregister().then(() => { - window.location.reload(); - }); - }); - } else { - // Service worker found. Proceed as normal. - registerValidSW(swUrl, config); - } - }) - .catch(() => { - console.log( - "No internet connection found. App is running in offline mode." - ); - }); -} - -export function unregister() { - if ("serviceWorker" in navigator) { - navigator.serviceWorker.ready - .then((registration) => { - registration.unregister(); - }) - .catch((error) => { - console.error(error.message); - }); - } -} diff --git a/frontend/src/setupTests.js b/frontend/src/setupTests.js deleted file mode 100644 index 9b1d6d0..0000000 --- a/frontend/src/setupTests.js +++ /dev/null @@ -1,5 +0,0 @@ -// jest-dom adds custom jest matchers for asserting on DOM nodes. -// allows you to do things like: -// expect(element).toHaveTextContent(/react/i) -// learn more: https://github.com/testing-library/jest-dom -import "@testing-library/jest-dom/extend-expect";
\ No newline at end of file diff --git a/frontend/src/utils.js b/frontend/src/utils.js index 0f0927e..445e576 100644 --- a/frontend/src/utils.js +++ b/frontend/src/utils.js @@ -133,7 +133,7 @@ export function getHeaderValue(request, key) { if (request && request.headers) { return request.headers[Object.keys(request.headers).find((k) => k.toLowerCase() === key.toLowerCase())]; } - return undefined; + return null; } export function downloadBlob(blob, fileName) { |