first commit

This commit is contained in:
2025-10-19 13:31:11 +00:00
commit 8bfc183b66
1248 changed files with 195992 additions and 0 deletions

2025
src/api/api.ts Executable file

File diff suppressed because it is too large Load Diff

553
src/api/dashboard.ts Executable file
View File

@@ -0,0 +1,553 @@
import { EntityType, PaginationResults, PolicyType } from "./explorer.ts";
import { Capacity } from "./user.ts";
import { TaskStatus, TaskSummary, TaskType } from "./workflow.ts";
export interface MetricsSummary {
dates: string[];
files: number[];
users: number[];
shares: number[];
file_total: number;
user_total: number;
share_total: number;
entities_total: number;
generated_at: string;
}
export interface Version {
version: string;
pro: boolean;
commit: string;
}
export interface HomepageSummary {
metrics_summary?: MetricsSummary;
site_urls: string[];
version: Version;
}
export interface ManualRefreshLicenseService {
license: string;
}
export interface GetSettingService {
keys: string[];
}
export interface SetSettingService {
settings: {
[key: string]: string;
};
}
export interface GroupEnt extends CommonMixin {
name: string;
max_storage?: number;
speed_limit?: number;
permissions?: string;
edges: {
storage_policies?: StoragePolicy;
};
total_users?: number;
settings?: GroupSetting;
}
export interface GroupSetting {
compress_size?: number;
decompress_size?: number;
remote_download_options?: Record<string, any>;
source_batch?: number;
aria2_batch?: number;
max_walked_files?: number;
trash_retention?: number;
redirected_source?: boolean;
}
export interface AdminListGroupResponse {
groups: GroupEnt[];
pagination: PaginationResults;
}
export interface QQConnectConfig {
app_id?: string;
app_secret?: string;
direct_sign_in?: boolean;
}
export interface LogtoConfig {
endpoint?: string;
app_id?: string;
app_secret?: string;
direct_sign_in?: boolean;
display_name?: string;
direct_sso?: string;
}
export interface FetchWOPIDiscoveryService {
endpoint: string;
}
export interface ThumbGeneratorTestService {
name: string;
executable: string;
}
export interface TestSMTPService {
to: string;
settings: {
[key: string]: string;
};
}
export enum QueueType {
IO_INTENSE = "io_intense",
MEDIA_META = "media_meta",
RECYCLE = "recycle",
THUMB = "thumb",
REMOTE_DOWNLOAD = "remote_download",
}
export interface QueueMetric {
name: QueueType;
busy_workers: number;
success_tasks: number;
failure_tasks: number;
submitted_tasks: number;
suspending_tasks: number;
}
export interface CommonMixin {
id: number;
created_at?: string;
updated_at?: string;
deleted_at?: string;
}
export interface StoragePolicy extends CommonMixin {
name: string;
type: PolicyType;
server?: string;
bucket_name?: string;
is_private?: boolean;
access_key?: string;
secret_key?: string;
max_size?: number;
auto_rename?: boolean;
dir_name_rule?: string;
file_name_rule?: string;
settings?: PolicySetting;
node_id?: number;
edges: {
users?: User[];
groups?: GroupEnt[];
node?: Node;
};
entities_count?: number;
entities_size?: number;
}
export enum NodeType {
master = "master",
slave = "slave",
}
export interface ListNodeResponse {
nodes: Node[];
pagination: PaginationResults;
}
export interface Node extends CommonMixin {
name?: string;
status?: NodeStatus;
type?: NodeType;
server?: string;
slave_key?: string;
capabilities?: string;
weight?: number;
settings?: NodeSetting;
edges: {
storage_policy?: StoragePolicy[];
};
}
export enum DownloaderProvider {
qbittorrent = "qbittorrent",
aria2 = "aria2",
}
export interface QBittorrentSetting {
server?: string;
user?: string;
password?: string;
options?: Record<string, string>;
temp_path?: string;
}
export interface Aria2Setting {
server?: string;
token?: string;
options?: Record<string, string>;
temp_path?: string;
}
export interface NodeSetting {
provider?: DownloaderProvider;
qbittorrent?: QBittorrentSetting;
aria2?: Aria2Setting;
interval?: number;
wait_for_seeding?: boolean;
}
export enum NodeStatus {
active = "active",
suspended = "suspended",
}
export interface PolicySetting {
token?: string;
file_type?: string[];
is_file_type_deny_list?: boolean;
file_regexp?: string;
is_name_regexp_deny_list?: boolean;
od_redirect?: string;
custom_proxy?: boolean;
proxy_server?: string;
internal_proxy?: boolean;
od_driver?: string;
region?: string;
server_side_endpoint?: string;
chunk_size?: number;
tps_limit?: number;
tps_limit_burst?: number;
s3_path_style?: boolean;
thumb_exts?: string[];
thumb_support_all_exts?: boolean;
thumb_max_size?: number;
relay?: boolean;
pre_allocate?: boolean;
media_meta_exts?: string[];
media_meta_generator_proxy?: boolean;
thumb_generator_proxy?: boolean;
native_media_processing?: boolean;
s3_delete_batch_size?: number;
stream_saver?: boolean;
use_cname?: boolean;
source_auth?: boolean;
qiniu_upload_cdn?: boolean;
chunk_concurrency?: number;
}
export interface User extends CommonMixin {
email: string;
nick: string;
password?: string;
settings?: UserSetting;
status?: UserStatus;
storage?: number;
avatar?: string;
credit?: number;
group_expires?: string;
notify_date?: string;
group_users?: number;
previous_group?: number;
unmanaged_email?: boolean;
edges: {
group?: GroupEnt;
storage_policy?: StoragePolicy;
openid?: OpenID[];
passkey?: Passkey[];
};
hash_id?: string;
two_fa_enabled?: boolean;
capacity?: Capacity;
}
export interface OpenID extends CommonMixin {
provider?: number;
}
export interface Passkey extends CommonMixin {
name?: string;
}
export enum UserStatus {
active = "active",
inactive = "inactive",
manual_banned = "manual_banned",
sys_banned = "sys_banned",
}
export interface UserSetting {
profile_off?: boolean;
preferred_policy?: number;
preferred_theme?: string;
version_retention?: boolean;
version_retention_ext?: string[];
version_retention_max?: number;
pined?: PinedFile[];
language?: string;
}
export interface PinedFile {
uri: string;
name?: string;
}
export interface ListStoragePolicyResponse {
policies: StoragePolicy[];
pagination: PaginationResults;
}
export interface AdminListService {
page: number;
page_size: number;
order_by: string;
order_direction: string;
conditions?: Record<string, string>;
searches?: Record<string, string>;
}
export interface UpsertStoragePolicyService {
policy: StoragePolicy;
}
export interface CreateStoragePolicyCorsService {
policy: StoragePolicy;
}
export interface OauthCredentialStatus {
last_refresh_time: string;
valid: boolean;
}
export interface GetOauthRedirectService {
id: number;
secret: string;
app_id: string;
}
export interface FinishOauthCallbackService {
code: string;
state: string;
}
export interface UpsertGroupService {
group: GroupEnt;
}
export interface TestNodeService {
node: Node;
}
export interface TestNodeDownloaderService extends TestNodeService {}
export interface UpsertNodeService extends TestNodeService {}
export interface ListUserResponse {
users: User[];
pagination: PaginationResults;
}
export interface UpsertUserService {
user: User;
password?: string;
two_fa?: string;
}
export interface BatchIDService {
ids: number[];
force?: boolean;
}
/*
FilePermissions struct {
Groups map[int]*boolset.BooleanSet `json:"groups,omitempty"`
Users map[int]*boolset.BooleanSet `json:"users,omitempty"`
}
*/
export interface File extends CommonMixin {
type?: number;
name?: string;
owner_id?: number;
size?: number;
primary_entity?: number;
file_children?: number;
is_symbolic?: boolean;
storage_policy_files?: number;
edges: {
owner?: User;
storage_policies?: StoragePolicy;
metadata?: Metadata[];
entities?: Entity[];
direct_links?: DirectLink[];
shares?: Share[];
};
user_hash_id?: string;
file_hash_id?: string;
direct_link_map?: Record<number, string>;
}
export interface DirectLink extends CommonMixin {
name?: string;
downloads?: number;
speed?: number;
}
export interface Entity extends CommonMixin {
type?: EntityType;
source?: string;
size?: number;
reference_count?: number;
storage_policy_entities?: number;
upload_session_id?: string;
edges: {
user?: User;
storage_policy?: StoragePolicy;
file?: File[];
};
user_hash_id?: string;
user_hash_id_map?: Record<number, string>;
}
export interface Metadata extends CommonMixin {
name?: string;
value?: string;
is_public?: boolean;
}
export interface ListFileResponse {
files: File[];
pagination: PaginationResults;
}
export interface UpsertFileService {
file: File;
}
export interface ListEntityResponse {
entities: Entity[];
pagination: PaginationResults;
}
export interface LogEntry {
category: number;
failed?: boolean;
error?: string;
user_agent?: string;
is_system?: boolean;
reason?: string;
email_to?: string;
email_title?: string;
original_name?: string;
new_name?: string;
from?: string;
to?: string;
entity_create_time?: string;
storage_policy_id?: string;
storage_policy_name?: string;
account_id?: number;
account?: string;
account_uri?: string;
payment_id?: number;
points_change?: number;
sku?: string;
storage_size?: number;
expire?: string;
group_id?: number;
group_id_from?: number;
direct_link_id?: string;
openid_provider?: number;
sub?: string;
name?: string;
passkey_id?: number;
exts?: Record<string, string>;
}
export interface AuditLog extends CommonMixin {
type?: number;
correlation_id?: string;
ip?: string;
content?: LogEntry;
edges: {
user?: User;
file?: File;
entity?: Entity;
share?: Share;
};
user_hash_id?: string;
}
export interface ListAuditLogResponse {
logs: AuditLog[];
pagination: PaginationResults;
}
export interface TaskPublicState {
error?: string;
error_history?: string[];
executed_duration?: number;
retry_count?: number;
resume_time?: number;
slave_task_props?: SlaveTaskProps;
}
export interface SlaveTaskProps {
node_id?: number;
master_site_url?: string;
master_site_id?: string;
master_site_version?: string;
}
export interface Task extends CommonMixin {
type?: string;
status?: TaskStatus;
public_state?: TaskPublicState;
private_state?: string;
correlation_id?: string;
user_tasks?: number;
edges: {
user?: User;
};
user_hash_id?: string;
task_hash_id?: string;
summary?: TaskSummary;
node?: Node;
}
export interface ListTaskResponse {
tasks: Task[];
pagination: PaginationResults;
}
export interface Share extends CommonMixin {
password?: string;
views?: number;
downloads?: number;
expires?: string;
remain_downloads?: number;
edges: {
user?: User;
file?: File;
};
user_hash_id?: string;
share_link?: string;
}
export interface ListShareResponse {
shares: Share[];
pagination: PaginationResults;
}
export interface CleanupTaskService {
not_after: string;
types?: TaskType[];
status?: TaskStatus[];
}

576
src/api/explorer.ts Executable file
View File

@@ -0,0 +1,576 @@
import { ListViewColumnSetting } from "../component/FileManager/Explorer/ListView/Column.tsx";
import { User } from "./user.ts";
export interface PaginationArgs {
page?: number;
page_size?: number;
order_by?: string;
order_direction?: string;
next_page_token?: string;
}
export interface ListFileService extends PaginationArgs {
uri: string;
}
export const FileType = {
file: 0,
folder: 1,
};
export interface FileResponse {
type: number;
id: string;
name: string;
created_at: string;
updated_at: string;
size: number;
metadata?: {
[key: string]: string;
};
path: string;
shared?: boolean;
capability?: string;
owned?: boolean;
folder_summary?: FolderSummary;
extended_info?: ExtendedInfo;
primary_entity?: string;
}
export interface FolderSummary {
size: number;
files: number;
folders: number;
completed: boolean;
calculated_at: string;
}
export interface ExtendedInfo {
storage_policy?: StoragePolicy;
storage_used: number;
shares?: Share[];
entities?: Entity[];
view?: ExplorerView;
direct_links?: DirectLink[];
}
export interface DirectLink {
id: string;
created_at: string;
url: string;
downloaded: number;
}
export interface Entity {
id: string;
type: number;
created_at: string;
storage_policy?: StoragePolicy;
size: number;
created_by?: User;
}
export interface Share {
id: string;
name?: string;
expires?: string;
is_private?: boolean;
share_view?: boolean;
remain_downloads?: number;
created_at?: string;
url: string;
visited: number;
downloaded: number;
expired?: boolean;
unlocked: boolean;
password_protected: boolean;
source_type?: number;
owner: User;
source_uri?: string;
password?: string;
show_readme?: boolean;
}
export enum PolicyType {
local = "local",
remote = "remote",
oss = "oss",
qiniu = "qiniu",
onedrive = "onedrive",
cos = "cos",
upyun = "upyun",
s3 = "s3",
ks3 = "ks3",
obs = "obs",
load_balance = "load_balance",
}
export interface StoragePolicy {
id: string;
name: string;
allowed_suffix?: string[];
denied_suffix?: string[];
allowed_name_regexp?: string;
denied_name_regexp?: string;
max_size: number;
type: PolicyType;
relay?: boolean;
chunk_concurrency?: number;
}
export interface PaginationResults {
page: number;
page_size: number;
total_items?: number;
next_token?: string;
is_cursor?: boolean;
}
export interface NavigatorProps {
capability?: string;
max_page_size: number;
order_by_options: string[];
order_direction_options: string[];
}
export interface ExplorerView {
page_size: number;
order?: string;
order_direction?: string;
view?: string;
thumbnail?: boolean;
columns?: ListViewColumnSetting[];
gallery_width?: number;
}
export interface ListResponse {
files: FileResponse[];
pagination: PaginationResults;
props: NavigatorProps;
context_hint?: string;
recursion_limit_reached?: boolean;
mixed_type?: boolean;
single_file_view?: boolean;
parent?: FileResponse;
storage_policy?: StoragePolicy;
view?: ExplorerView;
}
export const Metadata = {
share_redirect: "sys:shared_redirect",
share_owner: "sys:shared_owner",
upload_session_id: "sys:upload_session_id",
icon_color: "customize:icon_color",
emoji: "customize:emoji",
live_photo: "customize:live_photo",
tag_prefix: "tag:",
thumbDisabled: "thumb:disabled",
restore_uri: "sys:restore_uri",
expected_collect_time: "sys:expected_collect_time",
// Exif
gps_lng: "exif:longitude",
gps_lat: "exif:latitude",
gps_attitude: "exif:altitude",
artist: "exif:artist",
copy_right: "exif:copy_right",
camera_model: "exif:camera_model",
camera_make: "exif:camera_make",
camera_owner_name: "exif:camera_owner_name",
body_serial_number: "exif:body_serial_number",
lens_make: "exif:lens_make",
lens_model: "exif:lens_model",
software: "exif:software",
exposure_time: "exif:exposure_time",
f_number: "exif:f",
aperture_value: "exif:aperture_value",
focal_length: "exif:focal_length",
iso_speed_ratings: "exif:iso",
pixel_x_dimension: "exif:x",
pixel_y_dimension: "exif:y",
orientation: "exif:orientation",
taken_at: "exif:taken_at",
flash: "exif:flash",
image_description: "exif:image_description",
projection_type: "exif:projection_type",
exposure_bias_value: "exif:exposure_bias",
// Music
music_title: "music:title",
music_artist: "music:artist",
music_album: "music:album",
// Stream
stream_title: "stream:title",
stream_duration: "stream:duration",
stream_format_name: "stream:format",
stream_format_long: "stream:formatLong",
stream_bit_rate: "stream:bitrate",
stream_description: "stream:description",
stream_indexed_codec: "codec",
stream_indexed_bitrate: "bitrate",
stream_indexed_width: "width",
stream_indexed_height: "height",
// Geocoding
street: "geocoding:street",
locality: "geocoding:locality",
place: "geocoding:place",
district: "geocoding:district",
region: "geocoding:region",
country: "geocoding:country",
};
export interface FileThumbResponse {
url: string;
expires?: string;
}
export interface DeleteFileService {
uris: string[];
unlink?: boolean;
skip_soft_delete?: boolean;
}
export interface LockApplication {
type: string;
inner_xml?: string;
viewer_id?: string;
}
export interface LockOwner {
application: LockApplication;
}
export interface ConflictDetail {
path?: string;
token?: string;
owner?: LockOwner;
type: number;
}
export interface UnlockFileService {
tokens: string[];
}
export interface RenameFileService {
uri: string;
new_name: string;
}
export const NavigatorCapability = {
create_file: 0,
rename_file: 1,
upload_file: 6,
download_file: 7,
update_metadata: 8,
list_children: 9,
generate_thumb: 10,
delete_file: 14,
lock_file: 15,
soft_delete: 16,
restore: 17,
share: 18,
info: 19,
version_control: 20,
enter_folder: 23,
};
export interface PinFileService {
uri: string;
name?: string;
}
export interface MoveFileService extends MultipleUriService {
dst: string;
copy?: boolean;
}
export interface MetadataPatch {
key: string;
value?: string;
remove?: boolean;
}
export interface PatchMetadataService extends MultipleUriService {
patches: MetadataPatch[];
}
export interface ShareCreateService {
uri: string;
downloads?: number;
is_private?: boolean;
password?: string;
expire?: number;
share_view?: boolean;
show_readme?: boolean;
}
export interface CreateFileService {
uri: string;
type: "file" | "folder";
err_on_conflict?: boolean;
metadata?: {
[key: string]: string;
};
}
export interface FileURLService extends MultipleUriService {
download?: boolean;
redirect?: boolean;
entity?: string;
no_cache?: boolean;
skip_error?: boolean;
use_primary_site_url?: boolean;
archive?: boolean;
}
export interface FileURLResponse {
urls: EntityURLResponse[];
expires: string;
}
export interface EntityURLResponse {
url: string;
stream_saver_display_name?: string;
}
export interface GetFileInfoService {
uri?: string;
id?: string;
extended?: boolean;
folder_summary?: boolean;
}
export enum EntityType {
version = 0,
thumbnail = 1,
live_photo = 2,
}
export interface VersionControlService {
uri: string;
version: string;
}
export const AuditLogType = {
server_start: 0,
user_signup: 1,
email_sent: 2,
user_activated: 3,
user_login_failed: 4,
user_login: 5,
user_token_refresh: 6,
file_create: 7,
file_rename: 8,
set_file_permission: 9,
entity_uploaded: 10,
entity_downloaded: 11,
copy_from: 12,
copy_to: 13,
move_to: 14,
delete_file: 15,
move_to_trash: 16,
share: 17,
share_link_viewed: 18,
set_current_version: 19,
delete_version: 20,
thumb_generated: 21,
live_photo_uploaded: 22,
update_metadata: 23,
edit_share: 24,
delete_share: 25,
mount: 26,
relocate: 27,
create_archive: 28,
extract_archive: 29,
webdav_login_failed: 30,
webdav_account_create: 31,
webdav_account_update: 32,
webdav_account_delete: 33,
payment_created: 34,
points_change: 35,
payment_paid: 36,
payment_fulfilled: 37,
payment_fulfill_failed: 38,
storage_added: 39,
group_changed: 40,
user_exceed_quota_notified: 41,
user_changed: 42,
get_direct_link: 43,
link_account: 44,
unlink_account: 45,
change_nick: 46,
change_avatar: 47,
membership_unsubscribe: 48,
change_password: 49,
enable_2fa: 50,
disable_2fa: 51,
add_passkey: 52,
remove_passkey: 53,
redeem_gift_code: 54,
file_imported: 55,
update_view: 56,
delete_direct_link: 57,
report_abuse: 58,
};
export interface MultipleUriService {
uris: string[];
}
export const ViewerAction = {
edit: "edit",
view: "view",
};
export const ViewerType = {
builtin: "builtin",
wopi: "wopi",
custom: "custom",
};
export enum ViewerPlatform {
pc = "pc",
mobile = "mobile",
all = "all",
}
export interface Viewer {
id: string;
type: string;
display_name: string;
exts: string[];
icon: string;
url?: string;
max_size?: number;
disabled?: boolean;
props?: {
[key: string]: string;
};
wopi_actions?: {
[key: string]: {
[key: string]: string;
};
};
templates?: NewFileTemplate[];
platform?: ViewerPlatform;
required_group_permission?: number[];
}
export interface NewFileTemplate {
ext: string;
display_name: string;
}
export interface ViewerGroup {
viewers: Viewer[];
}
export interface FileUpdateService {
uri: string;
previous?: string;
}
export interface ViewerSession {
id: string;
access_token: string;
expires: number;
}
export interface ViewerSessionResponse {
session: ViewerSession;
wopi_src?: string;
}
export interface CreateViewerSessionService {
uri: string;
viewer_id: string;
preferred_action: string;
version?: string;
}
export interface UploadSessionRequest {
uri: string;
size: number;
policy_id: string;
last_modified?: number;
entity_type?: string;
metadata?: {
[key: string]: string;
};
mime_type?: string;
}
export interface UploadCredential {
session_id: string;
expires: number;
chunk_size: number;
upload_urls: string[];
credential: string;
uploadID: string;
callback: string;
ak: string;
keyTime: string;
path: string;
completeURL: string;
storage_policy?: StoragePolicy;
uri: string;
callback_secret: string;
mime_type?: string;
upload_policy?: string;
}
export interface DeleteUploadSessionService {
id: string;
uri: string;
}
export interface DirectLink {
file_url: string;
link: string;
}
export interface PatchViewSyncService {
uri: string;
view?: ExplorerView;
}
export interface CustomProps {
id: string;
name: string;
type: CustomPropsType;
max?: number;
min?: number;
default?: string;
options?: string[];
icon?: string;
}
export enum CustomPropsType {
text = "text",
number = "number",
boolean = "boolean",
select = "select",
multi_select = "multi_select",
user = "user",
link = "link",
rating = "rating",
}
export interface ArchivedFile {
name: string;
size: number;
updated_at?: string;
is_directory: boolean;
}
export interface ArchiveListFilesResponse {
files: ArchivedFile[];
}
export interface ArchiveListFilesService {
uri: string;
entity?: string;
text_encoding?: string;
}

283
src/api/request.ts Executable file
View File

@@ -0,0 +1,283 @@
import axios, { AxiosRequestConfig } from "axios";
import { enqueueSnackbar, SnackbarAction } from "notistack";
import { DefaultCloseAction, ErrorListDetailAction } from "../component/Common/Snackbar/snackbar.tsx";
import i18n from "../i18n.ts";
import { AppThunk } from "../redux/store.ts";
import { openLockConflictDialog } from "../redux/thunks/dialog.ts";
import { router } from "../router";
import SessionManager from "../session";
import { ErrNames } from "../session/errors.ts";
import { sendRefreshToken } from "./api.ts";
export interface requestOpts {
errorSnackbarMsg: (e: Error) => string;
bypassSnackbar?: (e: Error) => boolean;
noCredential: boolean;
skipBatchError?: boolean;
skipLockConflict?: boolean;
withHost?: boolean;
acceptBatchPartialSuccess?: boolean;
}
export const defaultOpts: requestOpts = {
errorSnackbarMsg: (e) => e.message,
noCredential: false,
skipBatchError: false,
};
export const ApiPrefix = "/api/v4";
const instance = axios.create({
baseURL: ApiPrefix,
});
export interface AggregatedError<T> {
[key: string]: Response<T>;
}
export interface Response<T> {
data: T;
code: number;
msg: string;
error?: string;
correlation_id?: string;
aggregated_error?: AggregatedError<T>;
}
export class AppError extends Error {
public code: any;
public cid: string | undefined = undefined;
public aggregatedError: AggregatedError<any> | undefined = undefined;
public rawMessage: string = "";
public error?: string = undefined;
public response: Response<any>;
constructor(resp: Response<any>) {
const message = resp.msg;
const code = resp.code;
super(message);
this.response = resp;
this.code = code;
const error = resp.error;
this.cid = resp.correlation_id;
this.aggregatedError = resp.aggregated_error;
this.rawMessage = message;
this.error = error;
if (i18n.exists(`errors.${code}`, { ns: "common" })) {
this.message = i18n.t(`errors.${code}`, {
ns: "common",
message,
});
} else if (i18n.exists(`errors.${code}`, { ns: "dashboard" })) {
this.message = i18n.t(`errors.${code}`, {
ns: "dashboard",
message,
});
} else {
this.message = message || i18n.t("unknownError", { ns: "common" });
}
this.message += error && !this.message.includes(error) ? ` (${error})` : "";
this.stack = new Error().stack;
}
ErrorResponse = (): Response<any> => {
return this.response;
};
}
//
// instance.interceptors.response.use(
// function (response: any) {
// response.rawData = response.data;
// response.data = response.data.data;
// if (
// response.rawData.code !== undefined &&
// response.rawData.code !== 0 &&
// response.rawData.code !== 203
// ) {
// // Login expired
// if (response.rawData.code === 401) {
// Auth.signout();
// window.location.href =
// "/login?redirect=" +
// encodeURIComponent(
// window.location.pathname + window.location.search
// );
// }
//
// // Non-admin
// if (response.rawData.code === 40008) {
// window.location.href = "/home";
// }
//
// // Not binding mobile phone
// if (response.rawData.code === 40010) {
// window.location.href = "/setting?modal=phone";
// }
// throw new AppError(
// response.rawData.msg,
// response.rawData.code,
// response.rawData.error
// );
// }
// return response;
// },
// function (error) {
// return Promise.reject(error);
// }
// );
export type ThunkResponse<T = any> = AppThunk<Promise<T>>;
export const Code = {
Success: 0,
Continue: 203,
CredentialInvalid: 40020,
IncorrectPassword: 40069,
LockConflict: 40073,
StaleVersion: 40076,
BatchOperationNotFullyCompleted: 40081,
DomainNotLicensed: 40087,
AnonymouseAccessDenied: 40088,
CodeLoginRequired: 401,
PermissionDenied: 403,
NodeFound: 404,
};
export const CrHeaderPrefix = "X-Cr-";
export const CrHeaders = {
context_hint: CrHeaderPrefix + "Context-Hint",
};
function getAccessToken(): AppThunk<Promise<string | undefined>> {
return async (dispatch, _getState) => {
try {
const accessToken = await SessionManager.getAccessToken();
return `Bearer ${accessToken}`;
} catch (e) {
if (!(e instanceof Error)) {
throw e;
}
switch (e.name) {
case ErrNames.ErrNoAvailableSession:
case ErrNames.ErrRefreshTokenExpired:
case ErrNames.ErrNoSesssionSelected:
return undefined;
case ErrNames.ErrAccessTokenExpired:
// try to refresh token
console.log("refresh access token");
const newToken = await dispatch(refreshToken());
return `Bearer ${newToken}`;
}
throw e;
}
};
}
function refreshToken(): AppThunk<Promise<string>> {
return async (dispatch, _getState) => {
const user = SessionManager.currentLogin();
const token = await dispatch(sendRefreshToken({ refresh_token: user.token.refresh_token }));
SessionManager.refreshToken(user.user.id, token);
return token.access_token;
};
}
let signOutLock = false;
export function send<T = any>(
url: string,
config?: AxiosRequestConfig,
opts: requestOpts = defaultOpts,
): ThunkResponse<T> {
return async (dispatch, _getState) => {
try {
let axiosConf: AxiosRequestConfig = {
...config,
headers: {
...config?.headers,
},
url,
};
if (!opts.noCredential) {
const token = await dispatch(getAccessToken());
if (token && axiosConf.headers) {
axiosConf.headers["Authorization"] = token;
}
}
const resp = await instance.request<Response<T>>(axiosConf);
if (resp.data.code !== undefined && resp.data.code !== Code.Success) {
switch (resp.data.code) {
case Code.CredentialInvalid:
case Code.CodeLoginRequired:
if (!signOutLock) {
SessionManager.signOutCurrent();
router.navigate(
"/session?redirect=" + encodeURIComponent(window.location.pathname + window.location.search),
);
}
signOutLock = true;
}
throw new AppError(resp.data);
}
return resp.data.data;
} catch (e) {
let partialSuccessResponse: any = undefined;
if (e instanceof Error) {
// Handle lock conflict error
if (e instanceof AppError && e.code == Code.LockConflict && !opts.skipLockConflict) {
let rejected = false;
try {
await dispatch(openLockConflictDialog(e.response));
} catch (e) {
rejected = true;
}
if (!rejected) {
return await dispatch(send(url, config, opts));
}
}
if (opts.bypassSnackbar && opts.bypassSnackbar(e)) {
throw e;
}
let action: SnackbarAction = DefaultCloseAction;
// Handle aggregated error
if (e instanceof AppError && e.code == Code.BatchOperationNotFullyCompleted) {
if (!opts.skipBatchError) {
action = ErrorListDetailAction(e.ErrorResponse());
if (opts.acceptBatchPartialSuccess) {
partialSuccessResponse = e.response.data;
}
} else {
const inner = e.aggregatedError?.[Object.keys(e.aggregatedError)[0]];
e = inner?.code ? new AppError(inner) : e;
}
}
if (e instanceof Error) {
enqueueSnackbar({
message: opts.errorSnackbarMsg(e),
variant: "error",
action,
});
}
}
if (partialSuccessResponse) {
return partialSuccessResponse;
}
throw e;
}
};
}
export const isRequestAbortedError = (e: Error) => {
return e.message == "Request aborted";
};

34
src/api/setting.ts Executable file
View File

@@ -0,0 +1,34 @@
import { PaginationResults } from "./explorer.ts";
export interface ListDavAccountsService {
page_size: number;
next_page_token?: string;
}
export interface DavAccount {
id: string;
created_at: string;
name: string;
uri: string;
password: string;
options?: string;
}
export interface ListDavAccountsResponse {
accounts: DavAccount[];
pagination?: PaginationResults;
}
export const DavAccountOption = {
readonly: 0,
proxy: 1,
disable_sys_files: 2,
};
export interface CreateDavAccountService {
name: string;
uri: string;
readonly?: boolean;
proxy?: boolean;
disable_sys_files?: boolean;
}

28
src/api/share.ts Executable file
View File

@@ -0,0 +1,28 @@
import { User } from "./user.ts";
import { PaginationResults, Share } from "./explorer.ts";
export interface ShareInfo {
id: string;
name?: string;
source_type?: number;
remain_downloads?: number;
visited?: number;
downloaded?: number;
expires?: string;
created_at?: string;
unlocked: boolean;
owner: User;
expired?: boolean;
}
export interface ListShareService {
page_size: number;
order_by?: string;
order_direction?: string;
next_page_token?: string;
}
export interface ListShareResponse {
shares: Share[];
pagination: PaginationResults;
}

66
src/api/site.ts Executable file
View File

@@ -0,0 +1,66 @@
import { CustomProps, ViewerGroup } from "./explorer.ts";
import { User } from "./user.ts";
export enum CaptchaType {
NORMAL = "normal",
RECAPTCHA = "recaptcha",
// Deprecated
TCAPTCHA = "tcaptcha",
TURNSTILE = "turnstile",
CAP = "cap",
}
export interface SiteConfig {
instance_id?: string;
title?: string;
login_captcha?: boolean;
reg_captcha?: boolean;
forget_captcha?: boolean;
themes?: string;
default_theme?: string;
authn?: boolean;
user?: User;
captcha_ReCaptchaKey?: string;
captcha_type?: CaptchaType;
turnstile_site_id?: string;
captcha_cap_instance_url?: string;
captcha_cap_site_key?: string;
captcha_cap_secret_key?: string;
captcha_cap_asset_server?: string;
register_enabled?: boolean;
logo?: string;
logo_light?: string;
tos_url?: string;
privacy_policy_url?: string;
icons?: string;
emoji_preset?: string;
map_provider?: string;
mapbox_ak?: string;
google_map_tile_type?: string;
file_viewers?: ViewerGroup[];
max_batch_size?: number;
app_promotion?: boolean;
thumbnail_width?: number;
thumbnail_height?: number;
custom_props?: CustomProps[];
custom_nav_items?: CustomNavItem[];
custom_html?: CustomHTML;
thumb_exts?: string[];
}
export interface CaptchaResponse {
ticket: string;
image: string;
}
export interface CustomNavItem {
name: string;
url: string;
icon: string;
}
export interface CustomHTML {
headless_footer?: string;
headless_bottom?: string;
sidebar_bottom?: string;
}

202
src/api/user.ts Executable file
View File

@@ -0,0 +1,202 @@
/**
* UserLoginService 管理用户登录的服务
*/
export interface UserLoginService {
email: string;
password: string;
otp?: string;
}
/**
* User 用户序列化器
*/
export interface User {
id: string;
email?: string;
nickname: string;
status?: any /* user.Status */;
avatar?: string;
created_at: any /* time.Time */;
preferred_theme?: string;
anonymous?: boolean;
group?: Group;
pined?: PinedFile[];
language?: string;
disable_view_sync?: boolean;
share_links_in_profile?: ShareLinksInProfileLevel;
}
export interface Group {
id: string;
name: string;
permission?: string;
direct_link_batch_size?: number;
trash_retention?: number;
}
export interface PinedFile {
uri: string;
name?: string;
}
export interface PrepareLoginResponse {
webauthn_enabled: boolean;
sso_enabled: boolean;
password_enabled: boolean;
qq_enabled: boolean;
}
export interface CaptchaRequest {
[key: string]: any;
}
export interface PasswordLoginRequest extends CaptchaRequest {
email: string;
password: string;
}
export interface Token {
access_token: string;
refresh_token: string;
access_expires: string;
refresh_expires: string;
}
export interface LoginResponse {
user: User;
token: Token;
}
export interface TwoFALoginRequest {
otp: string;
session_id: string;
}
export interface RefreshTokenRequest {
refresh_token: string;
}
export interface Capacity {
total: number;
used: number;
}
export const GroupPermission = {
is_admin: 0,
is_anonymous: 1,
share: 2,
webdav: 3,
archive_download: 4,
archive_task: 5,
webdav_proxy: 6,
share_download: 7,
remote_download: 9,
redirected_source: 11,
advance_delete: 12,
unique_direct_link: 17,
};
export interface UserSettings {
version_retention_enabled: boolean;
version_retention_ext?: string[];
version_retention_max?: number;
passwordless: boolean;
two_fa_enabled: boolean;
passkeys?: Passkey[];
disable_view_sync: boolean;
share_links_in_profile: ShareLinksInProfileLevel;
}
export interface PatchUserSetting {
nick?: string;
language?: string;
preferred_theme?: string;
version_retention_enabled?: boolean;
version_retention_ext?: string[];
version_retention_max?: number;
current_password?: string;
new_password?: string;
two_fa_enabled?: boolean;
two_fa_code?: string;
disable_view_sync?: boolean;
share_links_in_profile?: ShareLinksInProfileLevel;
}
export interface PasskeyCredentialOption {
publicKey: {
rp: {
name: string;
id: string;
};
user: {
name: string;
displayName: string;
id: string;
};
challenge: string;
pubKeyCredParams: {
type: "public-key";
alg: number;
}[];
timeout: number;
excludeCredentials: {
type: "public-key";
id: string;
}[];
authenticatorSelection: {
requireResidentKey: boolean;
userVerification: UserVerificationRequirement;
};
};
}
export interface PasskeyCredentialLoginOption {
publicKey: {
challenge: string;
timeout: number;
rpId: string;
};
}
export interface PreparePasskeyLoginResponse {
options: PasskeyCredentialLoginOption;
session_id: string;
}
export interface FinishPasskeyRegistrationService {
response: string;
name: string;
ua: string;
}
export interface Passkey {
id: string;
name: string;
created_at: string;
used_at: string;
}
export interface FinishPasskeyLoginService {
response: string;
session_id: string;
}
export interface SignUpService extends CaptchaRequest {
email: string;
password: string;
language: string;
}
export interface SendResetEmailService extends CaptchaRequest {
email: string;
}
export interface ResetPasswordService {
password: string;
secret: string;
}
export enum ShareLinksInProfileLevel {
public_share_only = "",
all_share = "all_share",
hide_share = "hide_share",
}

163
src/api/workflow.ts Executable file
View File

@@ -0,0 +1,163 @@
import { PaginationResults } from "./explorer.ts";
export interface ArchiveWorkflowService {
src: string[];
dst: string;
encoding?: string;
password?: string;
file_mask?: string[];
}
export interface TaskListResponse {
tasks: TaskResponse[];
pagination: PaginationResults;
}
export interface TaskResponse {
created_at: string;
updated_at: string;
id: string;
status: string;
type: string;
node?: NodeSummary;
summary?: TaskSummary;
error?: string;
error_history?: string[];
duration?: number;
resume_time?: number;
retry_count?: number;
}
export interface TaskSummary {
phase?: string;
props: {
src?: string;
src_str?: string;
dst?: string;
src_multiple?: string[];
dst_policy_id?: string;
failed?: number;
download?: DownloadTaskStatus;
};
}
export enum DownloadTaskState {
seeding = "seeding",
downloading = "downloading",
error = "error",
completed = "completed",
unknown = "unknown",
}
export interface DownloadTaskStatus {
name: string;
state: DownloadTaskState;
total: number;
downloaded: number;
download_speed: number;
upload_speed: number;
uploaded: number;
files?: DownloadTaskFile[];
hash?: string;
pieces?: string;
num_pieces?: number;
}
export interface DownloadTaskFile {
index: number;
name: string;
size: number;
progress: number;
selected: boolean;
}
export interface NodeSummary {
id: string;
name: string;
type: NodeTypes;
capabilities: string;
}
export enum NodeTypes {
master = "master",
slave = "slave",
}
export const NodeCapability = {
none: 0,
create_archive: 1,
extract_archive: 2,
remote_download: 3,
//relocate: 4,
};
export interface RelocateWorkflowService {
src: string[];
dst_policy_id: string;
}
export interface DownloadWorkflowService {
src?: string[];
src_file?: string;
dst: string;
}
export interface ImportWorkflowService {
src: string;
dst: string;
extract_media_meta?: boolean;
user_id: string;
recursive?: boolean;
policy_id: number;
}
export interface ListTaskService {
page_size: number;
category: ListTaskCategory;
next_page_token?: string;
}
export enum ListTaskCategory {
general = "general",
downloading = "downloading",
downloaded = "downloaded",
}
export enum TaskType {
create_archive = "create_archive",
extract_archive = "extract_archive",
remote_download = "remote_download",
media_metadata = "media_meta",
entity_recycle_routine = "entity_recycle_routine",
explicit_entity_recycle = "explicit_entity_recycle",
upload_sentinel_check = "upload_sentinel_check",
import = "import",
}
export enum TaskStatus {
queued = "queued",
processing = "processing",
suspending = "suspending",
error = "error",
canceled = "canceled",
completed = "completed",
}
export interface TaskProgress {
total: number;
current: number;
identifier: string;
}
export interface TaskProgresses {
[key: string]: TaskProgress;
}
export interface SetFileToDownloadArgs {
index: number;
download: boolean;
}
export interface SetDownloadFilesService {
files: SetFileToDownloadArgs[];
}