Skip to content
Open
Show file tree
Hide file tree
Changes from 43 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
1ce068d
Improved Error Messages in PreferencesComponent
BrianRaymond800 Apr 13, 2026
cb6aff1
Merge branch 'development' into issue1645-followup
aduques Jul 6, 2026
a0c9cd8
style: fix indentation in PreferencesComponent
GoodKimchi Jul 7, 2026
6c56579
Revert "style: fix indentation in PreferencesComponent"
GoodKimchi Jul 7, 2026
6a2ded3
Merge branch 'development' into issue1645-followup
aduques Jul 13, 2026
93d67ba
updated PreferencesComponent.tsx submitPreferences error message to b…
aduques Jul 13, 2026
124fba8
Merge branch 'issue1645-followup' of https://github.com/GoodKimchi/OE…
aduques Jul 14, 2026
1f77510
style: fix indentation in PreferencesComponent
GoodKimchi Jul 14, 2026
c640e63
fix: restore file size validation behavior
GoodKimchi Jul 15, 2026
2a45ffb
Merge branch 'development' into issue1645-followup
GoodKimchi Jul 15, 2026
1e4669d
Merge branch 'OpenEnergyDashboard:development' into issue1645-followup
aduques Jul 15, 2026
2b06772
merge conflicts
aduques Jul 15, 2026
e4ba5fe
refactor and add tests for time and duration validation
audreydng Apr 29, 2026
0b22fac
add maxLength and comments for non-moment time fields in csv pipeline
audreydng Apr 29, 2026
35a46f0
chore: remove csv pipeline changes (out of scope)
GoodKimchi Apr 30, 2026
260e524
improve time validation for route parameters
audreydng Jul 12, 2026
e45123f
add comments for time interval validation
audreydng Jul 13, 2026
9d82882
check non-string route input in time validation
audreydng Jul 14, 2026
a8129c0
resolved error that occurred from merge conflict
aduques Jul 15, 2026
23cb65c
Merge remote-tracking branch 'origin/issue1645-followup' into issue16…
GoodKimchi Jul 15, 2026
e4e2f7a
resolved errors from merge conflict (duplicated code) + restored chan…
aduques Jul 15, 2026
2f5d8e5
fix: restore infinity bound for file size limit
GoodKimchi Jul 16, 2026
dd15823
Merge branch 'development' into issue1645-followup
aduques Jul 16, 2026
fa217a4
fix: remove duplicate line after merge conflict
GoodKimchi Jul 16, 2026
11cbbf7
updated the login route to make sure the bcrypt.copare function is ca…
Andrew-Bonner Mar 31, 2026
717e557
call the bcrypt function without assigning it to the value and just s…
Andrew-Bonner Apr 21, 2026
c9ccbdb
Fix login timing for missing usernames
Adam-Alhakam Jul 14, 2026
6f42579
Address login timing review comments
Adam-Alhakam Jul 15, 2026
b68d776
Fix duplicate user declaration
Adam-Alhakam Jul 15, 2026
efd834e
Remove unused dummy hash constant
Adam-Alhakam Jul 16, 2026
d4fef5a
moved omit() calls from client to API layer for relevant files + adde…
aduques Jul 16, 2026
dfa8320
updated comments + removed extra whitespace
aduques Jul 16, 2026
4f8a490
small comment wording update
huss Jul 16, 2026
b6ddf63
updated failed.to.submit.changes + updated error notifications + remo…
aduques Jul 17, 2026
276c2a0
Merge branch 'development' into issue1645-followup
GoodKimchi Jul 22, 2026
5892a58
Merge branch 'development' into issue1645-followup
GoodKimchi Jul 22, 2026
6578318
Merge branch 'development' into issue1645-followup
aduques Jul 23, 2026
34d82d1
reverted specific error messages / second recheck of invalidFuncs + r…
aduques Jul 23, 2026
7f1f1e8
removed TODO DEBUG code
aduques Jul 23, 2026
6d836da
Merge branch 'development' into issue1645-followup
GoodKimchi Jul 27, 2026
357464a
Merge branch 'OpenEnergyDashboard:development' into issue1645-followup
GoodKimchi Jul 28, 2026
84e4940
Merge remote-tracking branch 'origin/issue1645-followup' into issue16…
GoodKimchi Jul 29, 2026
be43d7e
fix: share preferences file size validation limit
GoodKimchi Jul 30, 2026
61b53de
removed redundant min + conditional
aduques Aug 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 29 additions & 3 deletions src/client/app/components/admin/PreferencesComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { useTranslate } from '../../redux/componentHooks';
import TimeZoneSelect from '../TimeZoneSelect';
import { defaultAdminState } from '../../redux/slices/adminSlice';
import { checkboxStyle, labelStyle } from '../../styles/modalStyle';
import { MAX_FILE_SIZE_LIMIT } from '../../../../common/preferencesValidationConstants';

/**
* @returns Preferences Component for Administrative use
Expand Down Expand Up @@ -72,11 +73,13 @@ export default function PreferencesComponent() {

warningFileSize: (): boolean => {
return Number(localAdminPref.defaultWarningFileSize) < 0
|| Number(localAdminPref.defaultWarningFileSize) > MAX_FILE_SIZE_LIMIT
|| Number(localAdminPref.defaultWarningFileSize) > Number(localAdminPref.defaultFileSizeLimit);
},

fileSizeLimit: (): boolean => {
return Number(localAdminPref.defaultFileSizeLimit) < 0
|| Number(localAdminPref.defaultFileSizeLimit) > MAX_FILE_SIZE_LIMIT
|| Number(localAdminPref.defaultWarningFileSize) > Number(localAdminPref.defaultFileSizeLimit);
}
};
Expand Down Expand Up @@ -324,12 +327,28 @@ export default function PreferencesComponent() {
value={localAdminPref.defaultWarningFileSize}
onChange={e => makeLocalChanges('defaultWarningFileSize', Number(e.target.value))}
min='0'
max={Number(localAdminPref.defaultFileSizeLimit)}
max={Math.min(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm uncertain when the min is needed. It is not in the form feedback below. Since the defaultFileSizeLimit cannot exceed MAX_FILE_SIZE_LIMIT I thought that the check on defaultFileSizeLimit would be sufficient. What do you think?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that min in this case is redundant. I have removed the min property and have confirmed that you cannot type a value below 0 regardless. I believe that this min property was initially included to capture a case where the user might attempt to input a minimum value. I have not investigated what exactly prevents no negative values from being inputed, but I can confirm that the min property is not responsible and can be removed.

Number(localAdminPref.defaultFileSizeLimit),
MAX_FILE_SIZE_LIMIT
)}
maxLength={50}
invalid={invalidFuncs.warningFileSize()}
/>
<FormFeedback>
<FormattedMessage id="error.bounds" values={{ min: 0, max: Number(localAdminPref.defaultFileSizeLimit) }} />
{Number(localAdminPref.defaultWarningFileSize) < 0 ? (

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First, this new message is off since it indicated zero is not allowed. If that is why you added it then it can go. Second, I don't think it is needed. It isn't done on other pages. I tried removing the conditional and true part and I get all the messages desired. Thus, if not for the first item I still think this should go. Do you agree?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that since a value less than 0 cannot be typed (negative value), then there is no case that the "error.greater" message is displayed. Therefore, that condition is never reached. When the condition is removed, then the desired messages are still displayed. I have updated the FormFeedback line to the following:

  <FormFeedback>
				      <FormattedMessage
					      id="error.bounds"
					      values={{
						      min: 0,
						      max: Number(localAdminPref.defaultFileSizeLimit)
					      }}
				      />
  </FormFeedback>

<FormattedMessage
id="error.greater"
values={{ min: 0 }}
/>
) : (
<FormattedMessage
id="error.bounds"
values={{
min: 0,
max: Number(localAdminPref.defaultFileSizeLimit)
}}
/>
)}
</FormFeedback>
</div>
<div>
Expand All @@ -341,11 +360,18 @@ export default function PreferencesComponent() {
value={localAdminPref.defaultFileSizeLimit}
onChange={e => makeLocalChanges('defaultFileSizeLimit', Number(e.target.value))}
min={Number(localAdminPref.defaultWarningFileSize)}
max={MAX_FILE_SIZE_LIMIT}
maxLength={50}
invalid={invalidFuncs.fileSizeLimit()}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I played with this and it isn't showing the max warning as I had hoped. I debugged for a little while but not completely. It seems to relate to the fact that it is using a general function (makeLocalChanges) to handle changes and not one that thinks it is a number. This may unwind back to how it is stored in OED (here and/or server). I looked at src/client/app/components/meters/CreateMeterModalComponent.tsx and it does it differently but seems to check the max value.

Another issue is that src/server/routes/preferences.js has a max of 1000000000. I'm unsure where that came from. src/server/routes/meters.js on maxVal does not seem to have any limit and assumes it could not come through as a number if it was invalid (I think). I'm unclear on why OED has an upper limit on preferences and if the one in meter does what is desired. Again, it may relate to treating as a string instead of a number.

I could not add above so am putting here. The date check here is close to but a little different than meter. Unsure if it matters but wonder about standardizing them.

The old PR has a comment that also included unit for some of these. That may also help/need coordination.

I'm unsure the right overall solution for this and welcome thoughts.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @huss, @aduques and I dug into this a bit more and reproduced the issue. When entering a very large value, such as 9999999999999, for Default File Size Limit, the frontend accepts it without inline validation, but the backend rejects it with a Bad Request because preferences.js currently validates both file-size fields with a maximum of 1000000000.

I noticed you mentioned you were unsure where that limit came from, so I was thinking about a few possible directions:

  1. Keep the current maximum (1000000000) and align the frontend validation/messages with the backend.
  2. Keep a finite maximum, but define it as a shared/documented constant so the frontend and backend can't drift apart in the future.
  3. Remove the application-level maximum and allow any non-negative value if the intent is for deployments to determine their own upload limits.

Since these settings appear to be used mainly for CSV or spreadsheet imports, I would not expect most deployments to approach a 1 GB upload. That made me wonder whether the current limit is an intentional design decision or primarily a safeguard against unrealistic values. A shared constant seems maintainable and would be easy to update later if larger files need to be supported. I’m happy to implement whichever direction makes the most sense.

While tracing this, I also noticed what appears to be the same type of frontend/backend validation mismatch for Default Meter Reading Gap: the frontend effectively allows values above the backend maximum. I think that should probably be handled in a separate issue or PR to keep this one focused, but I wanted to mention it because it appears to be the same class of bug.
Screenshot 2026-07-28 235125

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@GoodKimchi & @aduques Thank you for thinking this through. My thoughts are below.

dug into this a bit more and reproduced the issue. When entering a very large value, such as 9999999999999, for Default File Size Limit, the frontend accepts it without inline validation, but the backend rejects it with a Bad Request because preferences.js currently validates both file-size fields with a maximum of 1000000000.

I noticed you mentioned you were unsure where that limit came from, so I was thinking about a few possible directions:

1. Keep the current maximum (1000000000) and align the frontend validation/messages with the backend.

2. Keep a finite maximum, but define it as a shared/documented constant so the frontend and backend can't drift apart in the future.

3. Remove the application-level maximum and allow any non-negative value if the intent is for deployments to determine their own upload limits.

Since these settings appear to be used mainly for CSV or spreadsheet imports, I would not expect most deployments to approach a 1 GB upload. That made me wonder whether the current limit is an intentional design decision or primarily a safeguard against unrealistic values. A shared constant seems maintainable and would be easy to update later if larger files need to be supported. I’m happy to implement whichever direction makes the most sense.

Yes. OED should not allow files that are too large. 1 GB seems more than enough. Aligning the front/back-end is a good idea and centralizing so they can never differ is even better. You can proceed with this unless you have thoughts/questions.

While tracing this, I also noticed what appears to be the same type of frontend/backend validation mismatch for Default Meter Reading Gap: the frontend effectively allows values above the backend maximum. I think that should probably be handled in a separate issue or PR to keep this one focused, but I wanted to mention it because it appears to be the same class of bug. Screenshot 2026-07-28 235125

Nice catch. I agree they should be consistent. Reading gap is in seconds so 86400 is 60 days. That was undoubtedly deliberate as a more than max value to ever use. I think it would stop really silly values while still allowing it to be so big that one can turn off the warnings about gaps. So, that value should consistently be used. Do you want to create the issue or should I? If you want to work on it then put in a comment on the new issue or let me know.

I welcome any thoughts.

@GoodKimchi GoodKimchi Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @huss . That all makes sense. @aduques and I will go ahead with the shared constant approach for the file-size validation so the frontend and backend stay in sync.
I'd also be happy to work on the Reading Gap validation afterward. I'll create a separate issue to keep this PR focused, then submit a follow-up PR for that consistency fix as well.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the guidance, @huss . We implemented the shared constant approach and pushed the changes.

The frontend and backend now both use a shared MAX_FILE_SIZE_LIMIT constant from src/common, so the validation is consistent and the frontend prevents values that the backend would reject.

We kept the Reading Gap changes out of this PR as discussed to keep it focused. @aduques has reviewed and tested the changes, so I believe it's ready for your review. I'll open a separate issue and follow-up PR for the Reading Gap consistency changes.

/>
<FormFeedback>
<FormattedMessage id="error.bounds" values={{ min: Number(localAdminPref.defaultWarningFileSize), max: Infinity }} />
<FormattedMessage
id="error.bounds"
values={{
min: Number(localAdminPref.defaultWarningFileSize),
max: MAX_FILE_SIZE_LIMIT
}}
/>
</FormFeedback>
</div>
<div>
Expand Down
4 changes: 2 additions & 2 deletions src/client/app/translations/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -824,7 +824,7 @@ const LocaleTranslationData = {
"failed.to.delete.map": "Échec de la suppression d'une carte",
"failed.to.edit.map": "Échec de la modification d'une carte",
"failed.to.link.graph": "Échec de lier le graphique",
"failed.to.submit.changes": "Échec de l'envoi des modifications",
"failed.to.submit.changes": "Échec de l'envoi des modifications ",
"false": "Faux",
"from.1.to.1000": "from 1 to 1000\u{26A1}",
"gps": "GPS: latitude, longitude\u{26A1}",
Expand Down Expand Up @@ -1433,7 +1433,7 @@ const LocaleTranslationData = {
"failed.to.delete.map": "No se pudo borrar el mapa",
"failed.to.edit.map": "No se pudo editar el mapa",
"failed.to.link.graph": "No se pudo vincular el gráfico",
"failed.to.submit.changes": "No se pudo entregar los cambios",
"failed.to.submit.changes": "No se pudo entregar los cambios ",
"false": "Falso",
"from.1.to.1000": "from 1 to 1000\u{26A1}",
"gps": "GPS: latitud, longitud",
Expand Down
7 changes: 7 additions & 0 deletions src/common/preferencesValidationConstants.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

export const MAX_FILE_SIZE_LIMIT: number;
13 changes: 13 additions & 0 deletions src/common/preferencesValidationConstants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/**
* Shared validation limits for Admin Preferences.
* Used by both frontend and backend to keep validation consistent.
*/
const PREFERENCES_VALIDATION_CONSTANTS = {
MAX_FILE_SIZE_LIMIT: 1000000000
};

module.exports = PREFERENCES_VALIDATION_CONSTANTS;
5 changes: 3 additions & 2 deletions src/server/routes/preferences.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const { getConnection } = require('../db');
const { STRING_GENERAL_MAX_LENGTH, STRING_SHORT_MAX_LENGTH: SHORT_STRING_MAX_LENGTH } = require('../util/validationConstants');
const { HTTP_CODES } = require('../util/httpCodes');
const { isValidIsoDateTime } = require('../util/timeValidation');
const { MAX_FILE_SIZE_LIMIT } = require('../../common/preferencesValidationConstants');

const router = express.Router();

Expand Down Expand Up @@ -66,12 +67,12 @@ router.post('/', adminAuthMiddleware('edit site preferences'), async (req, res)
defaultWarningFileSize: {
type: 'number',
minimum: 0,
maximum: 1000000000
maximum: MAX_FILE_SIZE_LIMIT
},
defaultFileSizeLimit: {
type: 'number',
minimum: 0,
maximum: 1000000000
maximum: MAX_FILE_SIZE_LIMIT
},
defaultAreaNormalization: {
type: 'boolean'
Expand Down
Loading