mirror of
https://github.com/orangecoding/fredy.git
synced 2026-06-16 12:31:07 +00:00
Improvements (#193)
* improving release banner * renaming general to settings * fixing working hours if they go to next day * fixing comparing versions * upgrade dependencies
This commit is contained in:
committed by
GitHub
parent
67af7c7dc5
commit
8324357edb
@@ -28,8 +28,6 @@ With a modern architecture, Fredy provides a **clean Web UI**, removes
|
||||
duplicates across platforms, and stores results so you never see the
|
||||
same listing twice.
|
||||
|
||||
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
||||
## ✨ Key Features
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import restana from 'restana';
|
||||
import fetch from 'node-fetch';
|
||||
import { getPackageVersion } from '../../utils.js';
|
||||
import semver from 'semver';
|
||||
|
||||
const service = restana();
|
||||
const versionRouter = service.newRouter();
|
||||
@@ -15,7 +16,7 @@ async function getCurrentVersionFromGithub() {
|
||||
const raw = await fetch('https://api.github.com/repos/orangecoding/fredy/releases/latest');
|
||||
const data = await raw.json();
|
||||
const localFredyVersion = await getPackageVersion();
|
||||
if (localFredyVersion === data.tag_name) {
|
||||
if (data.tag_name == null || semver.gte(localFredyVersion, data.tag_name)) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
|
||||
48
lib/utils.js
48
lib/utils.js
@@ -109,11 +109,22 @@ function timeStringToMs(timeString, now) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether current time is within configured working hours, or no hours are set.
|
||||
* If working hours are missing or incomplete, returns true.
|
||||
* @param {{workingHours?: {from?: string, to?: string}}} config
|
||||
* @param {number} now - Epoch ms
|
||||
* @returns {boolean}
|
||||
* Determine whether the given timestamp is within the configured working hours, or return true when the window is not set.
|
||||
* - If workingHours is missing or either 'from' or 'to' is empty/null, returns true.
|
||||
* - Supports windows that cross midnight (e.g., from '23:00' to '06:00').
|
||||
*
|
||||
* Time parsing is based on the local timezone of the running process.
|
||||
*
|
||||
* @param {{workingHours?: {from?: string|null, to?: string|null}}} config - Configuration object containing working hours in 'HH:mm' format.
|
||||
* @param {number} now - Epoch milliseconds to evaluate.
|
||||
* @returns {boolean} True when execution is allowed at 'now'.
|
||||
* @example
|
||||
* // Same-day window
|
||||
* duringWorkingHoursOrNotSet({ workingHours: { from: '08:00', to: '17:00' } }, someTime);
|
||||
* @example
|
||||
* // Window crossing midnight
|
||||
* // For { from: '05:00', to: '00:30' } → 23:00 => true, 01:00 => false, 06:00 => true
|
||||
* duringWorkingHoursOrNotSet({ workingHours: { from: '05:00', to: '00:30' } }, Date.now());
|
||||
*/
|
||||
function duringWorkingHoursOrNotSet(config, now) {
|
||||
const { workingHours } = config;
|
||||
@@ -122,7 +133,20 @@ function duringWorkingHoursOrNotSet(config, now) {
|
||||
}
|
||||
const toDate = timeStringToMs(workingHours.to, now);
|
||||
const fromDate = timeStringToMs(workingHours.from, now);
|
||||
return fromDate <= now && toDate >= now;
|
||||
|
||||
// If parsing fails (e.g., malformed time), be lenient and allow.
|
||||
if (isNaN(toDate) || isNaN(fromDate)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (toDate >= fromDate) {
|
||||
// Same-day window (e.g., 08:00 - 17:00)
|
||||
return now >= fromDate && now <= toDate;
|
||||
}
|
||||
|
||||
// Window crosses midnight (e.g., 05:00 -> 00:30 next day)
|
||||
// Accept if we are after 'from' today OR before 'to' today (which represents next day's cutoff).
|
||||
return now >= fromDate || now <= toDate;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -244,13 +268,13 @@ function sleep(ms) {
|
||||
}
|
||||
|
||||
/**
|
||||
* returns a random into between start and end
|
||||
* @param a start int
|
||||
* @param b max int
|
||||
* @returns {*}
|
||||
* Return a random integer between min and max (inclusive).
|
||||
* @param {number} min - Minimum integer value.
|
||||
* @param {number} max - Maximum integer value.
|
||||
* @returns {number} A random integer N where min <= N <= max.
|
||||
*/
|
||||
function randomBetween(a, b) {
|
||||
return Math.floor(Math.random() * (b - a + 1)) + a;
|
||||
function randomBetween(min, max) {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
|
||||
// Call refreshConfig() from the application entrypoint during startup to populate config.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "fredy",
|
||||
"version": "12.2.0",
|
||||
"version": "12.2.1",
|
||||
"description": "[F]ind [R]eal [E]states [d]amn eas[y].",
|
||||
"scripts": {
|
||||
"prepare": "husky",
|
||||
@@ -85,6 +85,7 @@
|
||||
"react-router": "7.9.2",
|
||||
"react-router-dom": "7.9.2",
|
||||
"restana": "5.1.0",
|
||||
"semver": "^7.7.2",
|
||||
"serve-static": "2.2.0",
|
||||
"slack": "11.0.2",
|
||||
"vite": "7.1.7",
|
||||
@@ -104,7 +105,7 @@
|
||||
"history": "5.3.0",
|
||||
"husky": "9.1.7",
|
||||
"less": "4.4.1",
|
||||
"lint-staged": "16.2.0",
|
||||
"lint-staged": "16.2.1",
|
||||
"mocha": "11.7.2",
|
||||
"nodemon": "^3.1.10",
|
||||
"prettier": "3.6.2"
|
||||
|
||||
@@ -8,6 +8,7 @@ const fakeWorkingHoursConfig = (from, to) => ({
|
||||
from,
|
||||
},
|
||||
});
|
||||
|
||||
describe('utils', () => {
|
||||
describe('#isOneOf()', () => {
|
||||
it('should be false', () => {
|
||||
@@ -33,5 +34,19 @@ describe('utils', () => {
|
||||
it('should be true if only from is set', () => {
|
||||
expect(duringWorkingHoursOrNotSet(fakeWorkingHoursConfig('12:00', null), 1622026740000)).to.be.true;
|
||||
});
|
||||
it('should handle working hours that cross midnight (e.g., 05:00 → 00:30)', () => {
|
||||
const cfg = fakeWorkingHoursConfig('05:00', '00:30');
|
||||
const mkTs = (h, m = 0) => {
|
||||
const d = new Date();
|
||||
d.setHours(h);
|
||||
d.setMinutes(m);
|
||||
d.setSeconds(0);
|
||||
d.setMilliseconds(0);
|
||||
return d.getTime();
|
||||
};
|
||||
expect(duringWorkingHoursOrNotSet(cfg, mkTs(23, 0))).to.be.true; // 23:00 => within window
|
||||
expect(duringWorkingHoursOrNotSet(cfg, mkTs(1, 0))).to.be.false; // 01:00 => outside window
|
||||
expect(duringWorkingHoursOrNotSet(cfg, mkTs(6, 0))).to.be.true; // 06:00 => within window
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -53,7 +53,7 @@ const TopMenu = function TopMenu({ isAdmin }) {
|
||||
tab={
|
||||
<span>
|
||||
<IconSetting />
|
||||
General
|
||||
Settings
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Banner, Descriptions } from '@douyinfe/semi-ui';
|
||||
import { useSelector } from '../../services/state/store.js';
|
||||
import { MarkdownRender } from '@douyinfe/semi-ui';
|
||||
|
||||
import './VersionBanner.less';
|
||||
|
||||
@@ -12,7 +13,7 @@ export default function VersionBanner() {
|
||||
type="success"
|
||||
icon={null}
|
||||
description={
|
||||
<div>
|
||||
<div style={{ overflow: 'auto' }}>
|
||||
<p>A new version of Fredy is available. Update now to take advantage of the latest features and bug fixes.</p>
|
||||
<Descriptions row size="small">
|
||||
<Descriptions.Item itemKey="Your Version">{versionUpdate.localFredyVersion}</Descriptions.Item>
|
||||
@@ -28,16 +29,9 @@ export default function VersionBanner() {
|
||||
<small>Release Notes</small>
|
||||
</b>
|
||||
</p>
|
||||
<pre>{stripFullChangelog(versionUpdate.body)}</pre>
|
||||
<MarkdownRender raw={versionUpdate.body} style={{ height: '200px' }} />
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
function stripFullChangelog(text) {
|
||||
if (text == null) {
|
||||
return '';
|
||||
}
|
||||
return text.replace(/(?:\r?\n)\*\*Full Changelog\*\*[\s\S]*$/u, '');
|
||||
}
|
||||
}
|
||||
|
||||
44
yarn.lock
44
yarn.lock
@@ -2538,16 +2538,16 @@ comma-separated-tokens@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz#4e89c9458acb61bc8fef19f4529973b2392839ee"
|
||||
integrity sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==
|
||||
|
||||
commander@14.0.1:
|
||||
version "14.0.1"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-14.0.1.tgz#2f9225c19e6ebd0dc4404dd45821b2caa17ea09b"
|
||||
integrity sha512-2JkV3gUZUVrbNA+1sjBOYLsMZ5cEEl8GTFP2a4AVz5hvasAMCQ1D2l2le/cX+pV4N6ZU17zjUahLpIXRrnWL8A==
|
||||
|
||||
commander@2:
|
||||
version "2.20.3"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
|
||||
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
|
||||
|
||||
commander@^14.0.1:
|
||||
version "14.0.1"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-14.0.1.tgz#2f9225c19e6ebd0dc4404dd45821b2caa17ea09b"
|
||||
integrity sha512-2JkV3gUZUVrbNA+1sjBOYLsMZ5cEEl8GTFP2a4AVz5hvasAMCQ1D2l2le/cX+pV4N6ZU17zjUahLpIXRrnWL8A==
|
||||
|
||||
compute-scroll-into-view@^1.0.20:
|
||||
version "1.0.20"
|
||||
resolved "https://registry.yarnpkg.com/compute-scroll-into-view/-/compute-scroll-into-view-1.0.20.tgz#1768b5522d1172754f5d0c9b02de3af6be506a43"
|
||||
@@ -4559,20 +4559,20 @@ lines-and-columns@^1.1.6:
|
||||
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
|
||||
integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
|
||||
|
||||
lint-staged@16.2.0:
|
||||
version "16.2.0"
|
||||
resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-16.2.0.tgz#ea7157bf007bdb50d2bb0559bc91c8e77d71c84b"
|
||||
integrity sha512-spdYSOCQ2MdZ9CM1/bu/kDmaYGsrpNOeu1InFFV8uhv14x6YIubGxbCpSmGILFoxkiheNQPDXSg5Sbb5ZuVnug==
|
||||
lint-staged@16.2.1:
|
||||
version "16.2.1"
|
||||
resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-16.2.1.tgz#bb82da8ce10059296b220f321980f0ee1ce40c28"
|
||||
integrity sha512-KMeYmH9wKvHsXdUp+z6w7HN3fHKHXwT1pSTQTYxB9kI6ekK1rlL3kLZEoXZCppRPXFK9PFW/wfQctV7XUqMrPQ==
|
||||
dependencies:
|
||||
commander "14.0.1"
|
||||
listr2 "9.0.4"
|
||||
micromatch "4.0.8"
|
||||
nano-spawn "1.0.3"
|
||||
pidtree "0.6.0"
|
||||
string-argv "0.3.2"
|
||||
yaml "2.8.1"
|
||||
commander "^14.0.1"
|
||||
listr2 "^9.0.4"
|
||||
micromatch "^4.0.8"
|
||||
nano-spawn "^1.0.3"
|
||||
pidtree "^0.6.0"
|
||||
string-argv "^0.3.2"
|
||||
yaml "^2.8.1"
|
||||
|
||||
listr2@9.0.4:
|
||||
listr2@^9.0.4:
|
||||
version "9.0.4"
|
||||
resolved "https://registry.yarnpkg.com/listr2/-/listr2-9.0.4.tgz#2916e633ae6e09d1a3f981172937ac1c5a8fa64f"
|
||||
integrity sha512-1wd/kpAdKRLwv7/3OKC8zZ5U8e/fajCfWMxacUvB79S5nLrYGPtUI/8chMQhn3LQjsRVErTb9i1ECAwW0ZIHnQ==
|
||||
@@ -5271,7 +5271,7 @@ micromark@^4.0.0:
|
||||
micromark-util-symbol "^2.0.0"
|
||||
micromark-util-types "^2.0.0"
|
||||
|
||||
micromatch@4.0.8:
|
||||
micromatch@^4.0.8:
|
||||
version "4.0.8"
|
||||
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202"
|
||||
integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==
|
||||
@@ -5401,7 +5401,7 @@ ms@^2.1.1, ms@^2.1.3:
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
|
||||
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
||||
|
||||
nano-spawn@1.0.3:
|
||||
nano-spawn@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/nano-spawn/-/nano-spawn-1.0.3.tgz#ef8d89a275eebc8657e67b95fc312a6527a05b8d"
|
||||
integrity sha512-jtpsQDetTnvS2Ts1fiRdci5rx0VYws5jGyC+4IYOTnIQ/wwdf6JdomlHBwqC3bJYOvaKu0C2GSZ1A60anrYpaA==
|
||||
@@ -5817,7 +5817,7 @@ picomatch@^4.0.3:
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042"
|
||||
integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==
|
||||
|
||||
pidtree@0.6.0:
|
||||
pidtree@^0.6.0:
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.6.0.tgz#90ad7b6d42d5841e69e0a2419ef38f8883aa057c"
|
||||
integrity sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==
|
||||
@@ -6835,7 +6835,7 @@ streamx@^2.15.0, streamx@^2.21.0:
|
||||
optionalDependencies:
|
||||
bare-events "^2.2.0"
|
||||
|
||||
string-argv@0.3.2:
|
||||
string-argv@^0.3.2:
|
||||
version "0.3.2"
|
||||
resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.2.tgz#2b6d0ef24b656274d957d54e0a4bbf6153dc02b6"
|
||||
integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==
|
||||
@@ -7583,7 +7583,7 @@ yallist@^3.0.2:
|
||||
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
|
||||
integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
|
||||
|
||||
yaml@2.8.1:
|
||||
yaml@^2.8.1:
|
||||
version "2.8.1"
|
||||
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.8.1.tgz#1870aa02b631f7e8328b93f8bc574fac5d6c4d79"
|
||||
integrity sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==
|
||||
|
||||
Reference in New Issue
Block a user