Skip to content

Commit 3a22682

Browse files
committed
feat: implement database backup service, API, and UI
1 parent 75922b5 commit 3a22682

21 files changed

Lines changed: 1016 additions & 257 deletions

File tree

app/client/src/api/api.ts

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1361,18 +1361,30 @@ export interface GlobalSetting {
13611361
* @memberof GlobalSetting
13621362
*/
13631363
'changedOpenApiKey'?: boolean;
1364-
/**
1365-
*
1366-
* @type {string}
1367-
* @memberof GlobalSetting
1368-
*/
1369-
'backupPath'?: string;
1370-
/**
1371-
*
1372-
* @type {number}
1373-
* @memberof GlobalSetting
1374-
*/
1375-
'backupKeepDays'?: number;
1364+
/**
1365+
*
1366+
* @type {string}
1367+
* @memberof GlobalSetting
1368+
*/
1369+
'backupPath'?: string;
1370+
/**
1371+
*
1372+
* @type {boolean}
1373+
* @memberof GlobalSetting
1374+
*/
1375+
'enableDatabaseBackup'?: boolean;
1376+
/**
1377+
*
1378+
* @type {number}
1379+
* @memberof GlobalSetting
1380+
*/
1381+
'backupKeepCount'?: number;
1382+
/**
1383+
*
1384+
* @type {string}
1385+
* @memberof GlobalSetting
1386+
*/
1387+
'backupTime'?: string;
13761388
/**
13771389
*
13781390
* @type {number}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import axios from "axios";
2+
3+
export interface DatabaseBackupInfo {
4+
fileName?: string;
5+
createdAt?: string;
6+
sizeBytes?: number;
7+
}
8+
9+
type ApiResult<T> = {
10+
code: number;
11+
message?: string;
12+
data: T;
13+
};
14+
15+
export async function fetchDatabaseBackups(): Promise<DatabaseBackupInfo[]> {
16+
const res = await axios.get<ApiResult<DatabaseBackupInfo[]>>("/api/setting/general/database-backups");
17+
if (res.data.code !== 0) {
18+
throw new Error(res.data.message || "Failed to fetch database backups.");
19+
}
20+
return res.data.data || [];
21+
}
22+
23+
export function getDatabaseBackupDownloadUrl(fileName: string): string {
24+
return `/api/setting/general/database-backups/download?fileName=${encodeURIComponent(fileName)}`;
25+
}

app/client/src/components/PageFilters.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ export type PageFilterOptions = {
6767
contentFilterType?: number,
6868
startDate?: string
6969
endDate?: string
70+
hideSortFilter?: boolean
71+
hideOrderFilter?: boolean
7072
showAllArticles?: boolean
7173
showAllArticlesOption?: boolean
7274
includeArchived?: boolean
@@ -92,7 +94,7 @@ function parseDateTimeString(dateStr: string | undefined) {
9294

9395
export default function PageFilters(props: PageFilterProps) {
9496
const {options, onChange} = props;
95-
const {sortFields, defaultSortValue, asc, hideContentTypeFilter, contentFilterType, startDate, endDate, showAllArticles, showAllArticlesOption, includeArchived, includeArchivedOption} = options;
97+
const {sortFields, defaultSortValue, asc, hideContentTypeFilter, contentFilterType, startDate, endDate, hideSortFilter, hideOrderFilter, showAllArticles, showAllArticlesOption, includeArchived, includeArchivedOption} = options;
9698
const [pickerAnchorEl, setPickerAnchorEl] = React.useState(null);
9799
const parsedStart = parseDateTimeString(startDate);
98100
const parsedEnd = parseDateTimeString(endDate);
@@ -227,8 +229,8 @@ export default function PageFilters(props: PageFilterProps) {
227229
const contentLabel = contentLabelMap[contentFilterType || 0];
228230
const sortLabel = getSortLabel(defaultSortValue);
229231
const orderLabel = asc ? t('page:sortOldest') : t('page:sortNewest');
230-
const showSort = sortFields.length > 1 || isPhone;
231-
const showOrder = defaultSortValue !== 'VOTE_SCORE';
232+
const showSort = !hideSortFilter && (sortFields.length > 1 || isPhone);
233+
const showOrder = !hideOrderFilter && defaultSortValue !== 'VOTE_SCORE';
232234
const hasFilterChanges = !isDeepEqual(initialOptionsRef.current, options);
233235

234236
function handleSortByChange(event, value) {

app/client/src/components/SearchBox.tsx

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ type SearchBoxProps = Readonly<{
3636
onSelectedKeywordsChange?: (keywords: string[]) => void;
3737
focusSignal?: number;
3838
defaultSearchText?: string;
39+
searchParamsToKeep?: string[];
3940
}>;
4041

4142
const defaultSearchOptions: SearchOption[] = [
@@ -107,7 +108,8 @@ export default function SearchBox({
107108
selectedKeywords,
108109
onSelectedKeywordsChange,
109110
focusSignal,
110-
defaultSearchText
111+
defaultSearchText,
112+
searchParamsToKeep = []
111113
}: SearchBoxProps) {
112114
const [focus, setFocus] = useState(false);
113115
const [params] = useSearchParams();
@@ -194,16 +196,24 @@ export default function SearchBox({
194196
return;
195197
}
196198

197-
// use set search params
199+
const nextSearchParams: Record<string, string> = {
200+
'q': submitText,
201+
'op': searchOptions
202+
.filter((option) => defaultSearchOptionKeywords.has(option.keyword))
203+
.map((option) => option.keyword)
204+
.join(',')
205+
};
206+
207+
searchParamsToKeep.forEach((paramName) => {
208+
const paramValue = params.get(paramName);
209+
if (paramValue) {
210+
nextSearchParams[paramName] = paramValue;
211+
}
212+
});
213+
198214
navigate({
199215
pathname: "/search",
200-
search: `?${createSearchParams({
201-
'q': submitText,
202-
'op': searchOptions
203-
.filter((option) => defaultSearchOptionKeywords.has(option.keyword))
204-
.map((option) => option.keyword)
205-
.join(',')
206-
})}`
216+
search: `?${createSearchParams(nextSearchParams)}`
207217
});
208218
}
209219

0 commit comments

Comments
 (0)