Skip to content

feat: auto-update #3815

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
105 changes: 105 additions & 0 deletions packages/service/common/system/info/controllers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { exit } from 'process';
import { compareVersion, getNextVersion, initScripts } from '../initScripts';
import { MongoSystemInfo } from './schema';

export async function checkSystemVersion() {
// 1. global.systemVersion is the newest version
// 2. version in db is the db's version
const info = await MongoSystemInfo.findOne();
const autoUpdate = process.env.AUTO_UPDATE === 'true';
if (info) {
const dbVersion = info.version;
const systemVersion = global.systemVersion;
if (compareVersion(dbVersion, systemVersion) < 0) {
console.info('System version is out of date, auto updating');
if (!autoUpdate) {
console.info('Please update the system manually');
return;
}
await MongoSystemInfo.findOneAndUpdate(
{
version: dbVersion
},
{
$set: {
version: global.systemVersion,
updateTime: new Date()
}
}
);
} else if (compareVersion(dbVersion, systemVersion) === 0) {
console.info('System version is up to date');
} else {
console.error('System downgrade is not permitted');
exit(1);
}
} else {
return await MongoSystemInfo.create({
version: global.systemVersion,
initScript: global.systemVersion
});
}
await runInitScript();
}

export async function runInitScript(retry = 0): Promise<boolean> {
const info = await MongoSystemInfo.findOne();
if (info) {
const initScriptVersion = info.initScript;
const nextVersion = getNextVersion(initScriptVersion);
if (!nextVersion) {
console.log('System is up to date');
return false;
}
const initFunc = initScripts[nextVersion];
if (initFunc) {
if (info.lock) {
console.log('System is updating');
return false;
}
await MongoSystemInfo.updateOne(
{ _id: info._id },
{
$set: {
lock: true
}
}
);
const result = await initFunc();
if (result) {
console.log('run init script success: ', nextVersion);
await MongoSystemInfo.updateOne(
{ _id: info._id },
{
$set: {
initScript: nextVersion,
lock: false
}
}
);
} else {
console.log('run init script failed: ', nextVersion);
console.log('retry: ', retry);
if (retry < 3) {
await runInitScript(retry + 1);
} else {
console.log('run init script failed, exit');
await MongoSystemInfo.updateOne(
{ _id: info._id },
{
$set: {
lock: false
}
}
);
exit(1);
}
}
}
const ret = await runInitScript();
if (!ret) {
return false;
}
}
return false;
}
25 changes: 25 additions & 0 deletions packages/service/common/system/info/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { getMongoModel, Schema } from '../../mongo';
import { SystemInfoSchemaType } from './type';
export const SystemInfoCollectionName = 'system_infos';

const SystemInfoSchema = new Schema({
version: {
type: String
},
initScript: {
type: String
},
updateTime: {
type: Date,
default: Date.now
},
lock: {
type: Boolean,
default: false
}
});

export const MongoSystemInfo = getMongoModel<SystemInfoSchemaType>(
SystemInfoCollectionName,
SystemInfoSchema
);
6 changes: 6 additions & 0 deletions packages/service/common/system/info/type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export type SystemInfoSchemaType = {
version: string;
initScript: string;
updateTime: Date;
lock: boolean;
};
57 changes: 57 additions & 0 deletions packages/service/common/system/initScripts/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
export const initScripts: Record<string, () => Promise<boolean>> = {
// '4.8.21': () => {
// console.log('initScripts v4.8.21');
// return Promise.resolve(true);
// },
// '4.8.22': () => {
// console.log('initScripts v4.8.22');
// return Promise.resolve(true);
// },
// '4.8.23': () => {
// console.log('initScripts v4.8.23');
// return Promise.resolve(true);
// },
// '4.8.24': () => {
// console.log('initScripts v4.8.24');
// return Promise.resolve(true);
// },
// '4.9.0': () => {
// console.log('initScripts v4.9.0');
// return Promise.resolve(true);
// },
// '4.9.1': () => {
// console.log('initScripts v4.9.1');
// return Promise.resolve(true);
// },
// '5.0.0': () => {
// console.log('initScripts v5.0.0');
// return Promise.resolve(true);
// }
};

export const versionList = Object.keys(initScripts);

export function compareVersion(v1: string, v2: string) {
// 1. change v4.8.21 to 4.8.21: remove v prefix
// 2. split by '.'
// 3. cast to number
const v1Arr = v1.replace('v', '').split('.').map(Number);
const v2Arr = v2.replace('v', '').split('.').map(Number);
for (let i = 0; i < v1Arr.length; i++) {
if (v1Arr[i] !== v2Arr[i]) {
return v1Arr[i] - v2Arr[i];
}
}
return 0;
}

export function getNextVersion(versionNow: string) {
const index = versionList.indexOf(versionNow);
if (index === -1) {
return versionList[0];
}
if (index === versionList.length - 1) {
return null;
}
return versionList[index + 1];
}
5 changes: 4 additions & 1 deletion projects/app/.env.template
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,7 @@ WORKFLOW_MAX_LOOP_TIMES=50
# # 日志来源ID前缀
# CHAT_LOG_SOURCE_ID_PREFIX=fastgpt-
# 自定义跨域,不配置时,默认都允许跨域(逗号分割)
ALLOWED_ORIGINS=
ALLOWED_ORIGINS=

# 自动更新
AUTO_UPDATE=
8 changes: 6 additions & 2 deletions projects/app/src/instrumentation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ export async function register() {
{ getSystemPluginCb },
{ startMongoWatch },
{ startCron },
{ startTrainingQueue }
{ startTrainingQueue },
{ checkSystemVersion }
] = await Promise.all([
import('@fastgpt/service/common/mongo/init'),
import('@fastgpt/service/common/system/tools'),
Expand All @@ -26,7 +27,8 @@ export async function register() {
import('@/service/core/app/plugin'),
import('@/service/common/system/volumnMongoWatch'),
import('@/service/common/system/cron'),
import('@/service/core/dataset/training/utils')
import('@/service/core/dataset/training/utils'),
import('@fastgpt/service/common/system/info/controllers')
]);

// 执行初始化流程
Expand All @@ -43,6 +45,8 @@ export async function register() {
initAppTemplateTypes();
getSystemPluginCb();
startMongoWatch();
await checkSystemVersion();

startCron();
startTrainingQueue(true);

Expand Down
40 changes: 40 additions & 0 deletions projects/app/src/pages/api/admin/update.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
import { NextAPI } from '@/service/middleware/entry';
import { MongoSystemInfo } from '@fastgpt/service/common/system/info/schema';
import { compareVersion } from '@fastgpt/service/common/system/initScripts';
import { runInitScript } from '@fastgpt/service/common/system/info/controllers';
export type UpdateQuery = {
version: string; // 升级到哪个版本
};
export type UpdateBody = {};
export type UpdateResponse = {};
async function handler(
req: ApiRequestProps<UpdateBody, UpdateQuery>,
_res: ApiResponseType<any>
): Promise<UpdateResponse> {
const { version } = req.query;
const systemInfo = await MongoSystemInfo.findOne();
if (!systemInfo) {
return Promise.reject(new Error('systemInfo not found'));
}
if (compareVersion(systemInfo.version, version) < 0) {
return Promise.reject(new Error('version is lower than current version'));
}

await MongoSystemInfo.updateOne(
{
_id: systemInfo._id
},
{
$set: {
version,
updateTime: new Date(),
lock: false
}
}
);

await runInitScript();
return {};
}
export default NextAPI(handler);
Loading