mirror of
https://github.com/orangecoding/fredy.git
synced 2026-06-16 12:31:07 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a7d0037edd | ||
|
|
f339a2e2cf |
@@ -1 +1 @@
|
||||
{"interval":"60","port":9998,"workingHours":{"from":"","to":""},"demoMode":false,"analyticsEnabled":null,"sqlitepath":"/db"}
|
||||
{"interval":"60","port":9998,"workingHours":{"from":"","to":""},"demoMode":false,"analyticsEnabled":true,"sqlitepath":"/db"}
|
||||
@@ -7,6 +7,7 @@ import { loginRouter } from './routes/loginRoute.js';
|
||||
import { config } from '../utils.js';
|
||||
import { userRouter } from './routes/userRoute.js';
|
||||
import { jobRouter } from './routes/jobRouter.js';
|
||||
import { versionRouter } from './routes/versionRouter.js';
|
||||
import bodyParser from 'body-parser';
|
||||
import restana from 'restana';
|
||||
import files from 'serve-static';
|
||||
@@ -23,6 +24,7 @@ service.use(cookieSession());
|
||||
service.use(staticService);
|
||||
service.use('/api/admin', authInterceptor());
|
||||
service.use('/api/jobs', authInterceptor());
|
||||
service.use('/api/version', authInterceptor());
|
||||
// /admin can only be accessed when user is having admin permissions
|
||||
service.use('/api/admin', adminInterceptor());
|
||||
service.use('/api/jobs/notificationAdapter', notificationAdapterRouter);
|
||||
@@ -30,6 +32,7 @@ service.use('/api/admin/generalSettings', generalSettingsRouter);
|
||||
service.use('/api/jobs/provider', providerRouter);
|
||||
service.use('/api/jobs/insights', analyticsRouter);
|
||||
service.use('/api/admin/users', userRouter);
|
||||
service.use('/api/version', versionRouter);
|
||||
service.use('/api/jobs', jobRouter);
|
||||
service.use('/api/login', loginRouter);
|
||||
//this route is unsecured intentionally as it is being queried from the login page
|
||||
|
||||
30
lib/api/routes/versionRouter.js
Normal file
30
lib/api/routes/versionRouter.js
Normal file
@@ -0,0 +1,30 @@
|
||||
import restana from 'restana';
|
||||
import fetch from 'node-fetch';
|
||||
import { getPackageVersion } from '../../utils.js';
|
||||
|
||||
const service = restana();
|
||||
const versionRouter = service.newRouter();
|
||||
|
||||
versionRouter.get('/', async (req, res) => {
|
||||
const versionPayload = await getCurrentVersionFromGithub();
|
||||
res.body = versionPayload == null ? { newVersion: false } : versionPayload;
|
||||
res.send();
|
||||
});
|
||||
|
||||
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) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
newVersion: true,
|
||||
version: data.tag_name,
|
||||
url: data.html_url,
|
||||
body: data.body,
|
||||
localFredyVersion,
|
||||
};
|
||||
}
|
||||
|
||||
export { versionRouter };
|
||||
@@ -1,9 +1,7 @@
|
||||
import { getJobs } from '../storage/jobStorage.js';
|
||||
import { getUniqueId } from './uniqueId.js';
|
||||
import { config, inDevMode } from '../../utils.js';
|
||||
import { config, getPackageVersion, inDevMode } from '../../utils.js';
|
||||
import os from 'os';
|
||||
import { readFileSync } from 'fs';
|
||||
import { packageUp } from 'package-up';
|
||||
import fetch from 'node-fetch';
|
||||
import logger from '../logger.js';
|
||||
|
||||
@@ -77,15 +75,3 @@ function enrichTrackingObject(trackingObject) {
|
||||
version,
|
||||
};
|
||||
}
|
||||
|
||||
async function getPackageVersion() {
|
||||
try {
|
||||
const packagePath = await packageUp();
|
||||
const packageJson = readFileSync(packagePath, 'utf8');
|
||||
const json = JSON.parse(packageJson);
|
||||
return json.version;
|
||||
} catch (error) {
|
||||
logger.error('Error reading version from package.json', error);
|
||||
}
|
||||
return 'N/A';
|
||||
}
|
||||
|
||||
20
lib/utils.js
20
lib/utils.js
@@ -3,8 +3,9 @@ import { fileURLToPath } from 'node:url';
|
||||
import { readFile } from 'fs/promises';
|
||||
import { createHash } from 'crypto';
|
||||
import { DEFAULT_CONFIG } from './defaultConfig.js';
|
||||
import fs from 'fs';
|
||||
import fs, { readFileSync } from 'fs';
|
||||
import logger from './services/logger.js';
|
||||
import { packageUp } from 'package-up';
|
||||
|
||||
const RE_GT = />/g;
|
||||
const RE_WEBP = /\/format\/webp/gi;
|
||||
@@ -196,6 +197,22 @@ const normalizeImageUrl = (url) => {
|
||||
return u;
|
||||
};
|
||||
|
||||
/**
|
||||
* returns Fredy's version
|
||||
* @returns {Promise<*|string>}
|
||||
*/
|
||||
async function getPackageVersion() {
|
||||
try {
|
||||
const packagePath = await packageUp();
|
||||
const packageJson = readFileSync(packagePath, 'utf8');
|
||||
const json = JSON.parse(packageJson);
|
||||
return json.version;
|
||||
} catch (error) {
|
||||
logger.error('Error reading version from package.json', error);
|
||||
}
|
||||
return 'N/A';
|
||||
}
|
||||
|
||||
await refreshConfig();
|
||||
|
||||
export { isOneOf };
|
||||
@@ -206,6 +223,7 @@ export { duringWorkingHoursOrNotSet };
|
||||
export { getDirName };
|
||||
export { config };
|
||||
export { buildHash };
|
||||
export { getPackageVersion };
|
||||
export default {
|
||||
isOneOf,
|
||||
nullOrEmpty,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "fredy",
|
||||
"version": "12.1.2",
|
||||
"version": "12.1.3",
|
||||
"description": "[F]ind [R]eal [E]states [d]amn eas[y].",
|
||||
"scripts": {
|
||||
"prepare": "husky",
|
||||
|
||||
@@ -18,11 +18,13 @@ import Jobs from './views/jobs/Jobs';
|
||||
import './App.less';
|
||||
import TrackingModal from './components/tracking/TrackingModal.jsx';
|
||||
import { Banner } from '@douyinfe/semi-ui';
|
||||
import VersionBanner from './components/version/VersionBanner.jsx';
|
||||
|
||||
export default function FredyApp() {
|
||||
const actions = useActions();
|
||||
const [loading, setLoading] = React.useState(true);
|
||||
const currentUser = useSelector((state) => state.user.currentUser);
|
||||
const versionUpdate = useSelector((state) => state.versionUpdate.versionUpdate);
|
||||
const settings = useSelector((state) => state.generalSettings.settings);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -34,6 +36,7 @@ export default function FredyApp() {
|
||||
await actions.jobs.getProcessingTimes();
|
||||
await actions.notificationAdapter.getAdapter();
|
||||
await actions.generalSettings.getGeneralSettings();
|
||||
await actions.versionUpdate.getVersionUpdate();
|
||||
}
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -53,7 +56,6 @@ export default function FredyApp() {
|
||||
<Route path="*" element={<Navigate to="/login" replace />} />
|
||||
</Routes>
|
||||
);
|
||||
|
||||
return loading ? null : needsLogin() ? (
|
||||
login()
|
||||
) : (
|
||||
@@ -62,7 +64,7 @@ export default function FredyApp() {
|
||||
<Logout />
|
||||
<Logo width={190} white />
|
||||
<Menu isAdmin={isAdmin()} />
|
||||
|
||||
{versionUpdate?.newVersion && <VersionBanner />}
|
||||
{settings.demoMode && (
|
||||
<>
|
||||
<Banner
|
||||
|
||||
43
ui/src/components/version/VersionBanner.jsx
Normal file
43
ui/src/components/version/VersionBanner.jsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import React from 'react';
|
||||
import { Banner, Descriptions } from '@douyinfe/semi-ui';
|
||||
import { useSelector } from '../../services/state/store.js';
|
||||
|
||||
import './VersionBanner.less';
|
||||
|
||||
export default function VersionBanner() {
|
||||
const versionUpdate = useSelector((state) => state.versionUpdate.versionUpdate);
|
||||
return (
|
||||
<Banner
|
||||
className="versionBanner"
|
||||
type="success"
|
||||
icon={null}
|
||||
description={
|
||||
<div>
|
||||
<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>
|
||||
<Descriptions.Item itemKey="Latest Version">{versionUpdate.version}</Descriptions.Item>
|
||||
<Descriptions.Item itemKey="Github Release">
|
||||
<a href={versionUpdate.url} target="_blank" rel="noreferrer">
|
||||
{versionUpdate.url}
|
||||
</a>{' '}
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
<p>
|
||||
<b>
|
||||
<small>Release Notes</small>
|
||||
</b>
|
||||
</p>
|
||||
<pre>{stripFullChangelog(versionUpdate.body)}</pre>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
function stripFullChangelog(text) {
|
||||
if (text == null) {
|
||||
return '';
|
||||
}
|
||||
return text.replace(/(?:\r?\n)\*\*Full Changelog\*\*[\s\S]*$/u, '');
|
||||
}
|
||||
}
|
||||
3
ui/src/components/version/VersionBanner.less
Normal file
3
ui/src/components/version/VersionBanner.less
Normal file
@@ -0,0 +1,3 @@
|
||||
.versionBanner {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
@@ -118,6 +118,18 @@ export const useFredyState = create(
|
||||
}
|
||||
},
|
||||
},
|
||||
versionUpdate: {
|
||||
async getVersionUpdate() {
|
||||
try {
|
||||
const response = await xhrGet('/api/version');
|
||||
set((state) => ({
|
||||
versionUpdate: { ...state.versionUpdate, versionUpdate: response.json },
|
||||
}));
|
||||
} catch (Exception) {
|
||||
console.error('Error while trying to get resource for api/version. Error:', Exception);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Initial state
|
||||
@@ -125,6 +137,7 @@ export const useFredyState = create(
|
||||
notificationAdapter: [],
|
||||
generalSettings: { settings: {} },
|
||||
demoMode: { demoMode: false },
|
||||
versionUpdate: {},
|
||||
provider: [],
|
||||
jobs: { jobs: [], insights: {}, processingTimes: {} },
|
||||
user: { users: [], currentUser: null },
|
||||
@@ -135,6 +148,7 @@ export const useFredyState = create(
|
||||
notificationAdapter: { ...effects.notificationAdapter },
|
||||
generalSettings: { ...effects.generalSettings },
|
||||
demoMode: { ...effects.demoMode },
|
||||
versionUpdate: { ...effects.versionUpdate },
|
||||
provider: { ...effects.provider },
|
||||
jobs: { ...effects.jobs },
|
||||
user: { ...effects.user },
|
||||
|
||||
Reference in New Issue
Block a user