fix(upload): throttle upload speed calculation to 100ms to avoid Infinity MB/s (#5456)

Co-authored-by: Henrique Dias <mail@hacdias.com>
This commit is contained in:
MSomnium Studios 2025-09-25 10:54:28 -04:00 committed by GitHub
parent b9787c78f3
commit 692ca5eaf0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 46 additions and 23 deletions

View File

@ -14,7 +14,7 @@
}} }}
</h2> </h2>
<div class="upload-info"> <div class="upload-info">
<div class="upload-speed">{{ speedMbytes }}/s</div> <div class="upload-speed">{{ speedText }}/s</div>
<div class="upload-eta">{{ formattedETA }} remaining</div> <div class="upload-eta">{{ formattedETA }} remaining</div>
<div class="upload-percentage">{{ sentPercent }}% Completed</div> <div class="upload-percentage">{{ sentPercent }}% Completed</div>
<div class="upload-fraction"> <div class="upload-fraction">
@ -88,6 +88,7 @@ const uploadStore = useUploadStore();
const { sentBytes, totalBytes } = storeToRefs(uploadStore); const { sentBytes, totalBytes } = storeToRefs(uploadStore);
const byteToMbyte = partial({ exponent: 2 }); const byteToMbyte = partial({ exponent: 2 });
const byteToKbyte = partial({ exponent: 1 });
const sentPercent = computed(() => const sentPercent = computed(() =>
((uploadStore.sentBytes / uploadStore.totalBytes) * 100).toFixed(2) ((uploadStore.sentBytes / uploadStore.totalBytes) * 100).toFixed(2)
@ -95,11 +96,33 @@ const sentPercent = computed(() =>
const sentMbytes = computed(() => byteToMbyte(uploadStore.sentBytes)); const sentMbytes = computed(() => byteToMbyte(uploadStore.sentBytes));
const totalMbytes = computed(() => byteToMbyte(uploadStore.totalBytes)); const totalMbytes = computed(() => byteToMbyte(uploadStore.totalBytes));
const speedMbytes = computed(() => byteToMbyte(speed.value)); const speedText = computed(() => {
const bytes = speed.value;
if (bytes < 1024 * 1024) {
const kb = parseFloat(byteToKbyte(bytes));
return `${kb.toFixed(2)} KB`;
} else {
const mb = parseFloat(byteToMbyte(bytes));
return `${mb.toFixed(2)} MB`;
}
});
let lastSpeedUpdate: number = 0; let lastSpeedUpdate: number = 0;
let recentSpeeds: number[] = []; let recentSpeeds: number[] = [];
let lastThrottleTime = 0;
const throttledCalculateSpeed = (sentBytes: number, oldSentBytes: number) => {
const now = Date.now();
if (now - lastThrottleTime < 100) {
return;
}
lastThrottleTime = now;
calculateSpeed(sentBytes, oldSentBytes);
};
const calculateSpeed = (sentBytes: number, oldSentBytes: number) => { const calculateSpeed = (sentBytes: number, oldSentBytes: number) => {
// Reset the state when the uploads batch is complete // Reset the state when the uploads batch is complete
if (sentBytes === 0) { if (sentBytes === 0) {
@ -149,7 +172,7 @@ const calculateEta = () => {
eta.value = remainingSize / speedBytesPerSecond; eta.value = remainingSize / speedBytesPerSecond;
}; };
watch(sentBytes, calculateSpeed); watch(sentBytes, throttledCalculateSpeed);
watch(totalBytes, (totalBytes, oldTotalBytes) => { watch(totalBytes, (totalBytes, oldTotalBytes) => {
if (oldTotalBytes !== 0) { if (oldTotalBytes !== 0) {

View File

@ -21,16 +21,16 @@ const (
) )
type userInfo struct { type userInfo struct {
ID uint `json:"id"` ID uint `json:"id"`
Locale string `json:"locale"` Locale string `json:"locale"`
ViewMode users.ViewMode `json:"viewMode"` ViewMode users.ViewMode `json:"viewMode"`
SingleClick bool `json:"singleClick"` SingleClick bool `json:"singleClick"`
Perm users.Permissions `json:"perm"` Perm users.Permissions `json:"perm"`
Commands []string `json:"commands"` Commands []string `json:"commands"`
LockPassword bool `json:"lockPassword"` LockPassword bool `json:"lockPassword"`
HideDotfiles bool `json:"hideDotfiles"` HideDotfiles bool `json:"hideDotfiles"`
DateFormat bool `json:"dateFormat"` DateFormat bool `json:"dateFormat"`
Username string `json:"username"` Username string `json:"username"`
AceEditorTheme string `json:"aceEditorTheme"` AceEditorTheme string `json:"aceEditorTheme"`
} }
@ -191,16 +191,16 @@ func renewHandler(tokenExpireTime time.Duration) handleFunc {
func printToken(w http.ResponseWriter, _ *http.Request, d *data, user *users.User, tokenExpirationTime time.Duration) (int, error) { func printToken(w http.ResponseWriter, _ *http.Request, d *data, user *users.User, tokenExpirationTime time.Duration) (int, error) {
claims := &authToken{ claims := &authToken{
User: userInfo{ User: userInfo{
ID: user.ID, ID: user.ID,
Locale: user.Locale, Locale: user.Locale,
ViewMode: user.ViewMode, ViewMode: user.ViewMode,
SingleClick: user.SingleClick, SingleClick: user.SingleClick,
Perm: user.Perm, Perm: user.Perm,
LockPassword: user.LockPassword, LockPassword: user.LockPassword,
Commands: user.Commands, Commands: user.Commands,
HideDotfiles: user.HideDotfiles, HideDotfiles: user.HideDotfiles,
DateFormat: user.DateFormat, DateFormat: user.DateFormat,
Username: user.Username, Username: user.Username,
AceEditorTheme: user.AceEditorTheme, AceEditorTheme: user.AceEditorTheme,
}, },
RegisteredClaims: jwt.RegisteredClaims{ RegisteredClaims: jwt.RegisteredClaims{