@putkoff/abstract-securefiles 0.0.32 → 0.0.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/css.d.ts +0 -0
- package/dist/App/SecureFiles.d.ts +4 -0
- package/dist/App/index.d.ts +1 -0
- package/dist/FileHandlers/FileRow/imports.d.ts +2 -0
- package/dist/FileHandlers/FileRow/index.d.ts +1 -0
- package/dist/FileHandlers/FileRow/src/index.d.ts +1 -0
- package/dist/FileHandlers/FileRow/src/row_utils.d.ts +2 -0
- package/dist/FileHandlers/FileTable/imports.d.ts +4 -0
- package/dist/FileHandlers/FileTable/index.d.ts +1 -0
- package/dist/FileHandlers/FileTable/src/index.d.ts +1 -0
- package/dist/FileHandlers/FileTable/src/table_utils.d.ts +2 -0
- package/dist/FileHandlers/ShareModal/imports.d.ts +2 -0
- package/dist/FileHandlers/ShareModal/index.d.ts +1 -0
- package/dist/FileHandlers/ShareModal/src/index.d.ts +1 -0
- package/dist/FileHandlers/ShareModal/src/share_utils.d.ts +2 -0
- package/dist/FileHandlers/Upload/imports.d.ts +2 -0
- package/dist/FileHandlers/Upload/index.d.ts +1 -0
- package/dist/FileHandlers/Upload/src/index.d.ts +2 -0
- package/dist/FileHandlers/Upload/src/upload_utils.d.ts +2 -0
- package/dist/FileHandlers/imports.d.ts +4 -0
- package/dist/FileHandlers/index.d.ts +4 -0
- package/dist/UI/index.d.ts +28 -0
- package/dist/file_utils/imports.d.ts +2 -0
- package/dist/file_utils/index.d.ts +1 -0
- package/dist/file_utils/src/cookieFetch.d.ts +22 -0
- package/dist/file_utils/src/delete_utils/imports.d.ts +1 -0
- package/dist/file_utils/src/delete_utils/index.d.ts +1 -0
- package/dist/file_utils/src/delete_utils/src/delete_row.d.ts +2 -0
- package/dist/file_utils/src/delete_utils/src/delete_select.d.ts +4 -0
- package/dist/file_utils/src/delete_utils/src/delete_selected.d.ts +2 -0
- package/dist/file_utils/src/delete_utils/src/index.d.ts +3 -0
- package/dist/file_utils/src/download_utils/imports.d.ts +2 -0
- package/dist/file_utils/src/download_utils/index.d.ts +1 -0
- package/dist/file_utils/src/download_utils/src/direct_download.d.ts +3 -0
- package/dist/file_utils/src/download_utils/src/download_file.d.ts +6 -0
- package/dist/file_utils/src/download_utils/src/download_select.d.ts +2 -0
- package/dist/file_utils/src/download_utils/src/index.d.ts +5 -0
- package/dist/file_utils/src/download_utils/src/target_downloads.d.ts +4 -0
- package/dist/file_utils/src/download_utils/src/trigger_download.d.ts +2 -0
- package/dist/file_utils/src/imports.d.ts +2 -0
- package/dist/file_utils/src/index.d.ts +6 -0
- package/dist/file_utils/src/list_utils/imports.d.ts +2 -0
- package/dist/file_utils/src/list_utils/index.d.ts +1 -0
- package/dist/file_utils/src/list_utils/src/index.d.ts +1 -0
- package/dist/file_utils/src/list_utils/src/list_utils.d.ts +2 -0
- package/dist/file_utils/src/select_utils/index.d.ts +1 -0
- package/dist/file_utils/src/select_utils/src/index.d.ts +3 -0
- package/dist/file_utils/src/select_utils/src/init_select_all.d.ts +2 -0
- package/dist/file_utils/src/select_utils/src/sync_header_checkbox.d.ts +5 -0
- package/dist/file_utils/src/select_utils/src/toggle_checkboxes.d.ts +2 -0
- package/dist/file_utils/src/share_utils/imports.d.ts +2 -0
- package/dist/file_utils/src/share_utils/index.d.ts +1 -0
- package/dist/file_utils/src/share_utils/src/index.d.ts +1 -0
- package/dist/file_utils/src/share_utils/src/share_utils.d.ts +7 -0
- package/{package.tmp.json → dist/file_utils/src/upload_utils/imports.d.ts} +0 -0
- package/dist/file_utils/src/upload_utils/index.d.ts +1 -0
- package/dist/file_utils/src/upload_utils/src/index.d.ts +3 -0
- package/dist/file_utils/src/upload_utils/src/patch_file_settings.d.ts +2 -0
- package/dist/file_utils/src/upload_utils/src/upload_file.d.ts +2 -0
- package/dist/file_utils/src/upload_utils/src/upload_files.d.ts +5 -0
- package/dist/hooks/imports.d.ts +3 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/useFiles/imports.d.ts +2 -0
- package/dist/hooks/useFiles/index.d.ts +1 -0
- package/dist/hooks/useFiles/src/deleteFile.d.ts +3 -0
- package/dist/hooks/useFiles/src/index.d.ts +6 -0
- package/dist/hooks/useFiles/src/load.d.ts +3 -0
- package/dist/hooks/useFiles/src/patchShareSettings.d.ts +3 -0
- package/dist/hooks/useFiles/src/toggleShare.d.ts +3 -0
- package/dist/hooks/useFiles/src/useFiles.d.ts +13 -0
- package/dist/hooks/useFiles/src/useSaveShareSettings.d.ts +7 -0
- package/dist/imports.d.ts +7 -0
- package/dist/index.cjs +992 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +1 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.js +945 -10
- package/dist/index.js.map +1 -1
- package/dist/styles/index.d.ts +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/types.d.ts +75 -0
- package/index.ts +0 -0
- package/package.json +4 -4
- package/postcss.config.cjs +0 -0
- package/rollup.config.js +0 -0
- package/rollup.config.mjs +0 -0
- package/src/App/SecureFiles.tsx +45 -0
- package/src/App/index.ts +1 -0
- package/src/FileHandlers/FileRow/imports.ts +2 -0
- package/src/FileHandlers/FileRow/index.ts +1 -0
- package/src/FileHandlers/FileRow/src/index.ts +1 -0
- package/src/FileHandlers/FileRow/src/row_utils.tsx +143 -0
- package/src/FileHandlers/FileTable/imports.ts +16 -0
- package/src/FileHandlers/FileTable/index.ts +1 -0
- package/src/FileHandlers/FileTable/src/index.ts +1 -0
- package/src/FileHandlers/FileTable/src/table_utils.tsx +251 -0
- package/src/FileHandlers/ShareModal/imports.ts +2 -0
- package/src/FileHandlers/ShareModal/index.ts +1 -0
- package/src/FileHandlers/ShareModal/src/index.ts +1 -0
- package/src/FileHandlers/ShareModal/src/share_utils.tsx +155 -0
- package/src/FileHandlers/Upload/imports.ts +3 -0
- package/src/FileHandlers/Upload/index.ts +1 -0
- package/src/FileHandlers/Upload/src/index.ts +2 -0
- package/src/FileHandlers/Upload/src/upload_utils.tsx +267 -0
- package/src/FileHandlers/imports.ts +18 -0
- package/src/FileHandlers/index.ts +4 -0
- package/src/UI/index.tsx +122 -0
- package/src/custom.d.ts +7 -0
- package/src/file_utils/imports.ts +2 -0
- package/src/file_utils/index.ts +1 -0
- package/src/file_utils/src/cookieFetch.ts +78 -0
- package/src/file_utils/src/delete_utils/imports.ts +1 -0
- package/src/file_utils/src/delete_utils/index.ts +1 -0
- package/src/file_utils/src/delete_utils/src/delete_row.ts +11 -0
- package/src/file_utils/src/delete_utils/src/delete_select.ts +10 -0
- package/src/file_utils/src/delete_utils/src/delete_selected.ts +15 -0
- package/src/file_utils/src/delete_utils/src/index.ts +3 -0
- package/src/file_utils/src/download_utils/imports.ts +3 -0
- package/src/file_utils/src/download_utils/index.ts +1 -0
- package/src/file_utils/src/download_utils/src/direct_download.ts +22 -0
- package/src/file_utils/src/download_utils/src/download_file.ts +25 -0
- package/src/file_utils/src/download_utils/src/download_select.ts +20 -0
- package/src/file_utils/src/download_utils/src/index.ts +5 -0
- package/src/file_utils/src/download_utils/src/target_downloads.ts +21 -0
- package/src/file_utils/src/download_utils/src/trigger_download.ts +14 -0
- package/src/file_utils/src/imports.ts +3 -0
- package/src/file_utils/src/index.ts +6 -0
- package/src/file_utils/src/list_utils/imports.ts +2 -0
- package/src/file_utils/src/list_utils/index.ts +1 -0
- package/src/file_utils/src/list_utils/src/index.ts +1 -0
- package/src/file_utils/src/list_utils/src/list_utils.ts +28 -0
- package/src/file_utils/src/select_utils/index.ts +1 -0
- package/src/file_utils/src/select_utils/src/index.ts +3 -0
- package/src/file_utils/src/select_utils/src/init_select_all.ts +37 -0
- package/src/file_utils/src/select_utils/src/sync_header_checkbox.ts +18 -0
- package/src/file_utils/src/select_utils/src/toggle_checkboxes.ts +11 -0
- package/src/file_utils/src/share_utils/imports.ts +2 -0
- package/src/file_utils/src/share_utils/index.ts +1 -0
- package/src/file_utils/src/share_utils/src/index.ts +1 -0
- package/src/file_utils/src/share_utils/src/share_utils.ts +20 -0
- package/src/file_utils/src/upload_utils/imports.ts +1 -0
- package/src/file_utils/src/upload_utils/index.ts +1 -0
- package/src/file_utils/src/upload_utils/src/index.ts +3 -0
- package/src/file_utils/src/upload_utils/src/patch_file_settings.ts +20 -0
- package/src/file_utils/src/upload_utils/src/upload_file.ts +27 -0
- package/src/file_utils/src/upload_utils/src/upload_files.ts +19 -0
- package/src/global.d.ts +1 -0
- package/src/hooks/imports.ts +3 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useFiles/imports.ts +3 -0
- package/src/hooks/useFiles/index.ts +1 -0
- package/src/hooks/useFiles/src/deleteFile.ts +23 -0
- package/src/hooks/useFiles/src/index.ts +6 -0
- package/src/hooks/useFiles/src/load.ts +39 -0
- package/src/hooks/useFiles/src/logs/clipit_logs.log +0 -0
- package/src/hooks/useFiles/src/logs/routes_bp.log +0 -0
- package/src/hooks/useFiles/src/patchShareSettings.ts +27 -0
- package/src/hooks/useFiles/src/toggleShare.ts +28 -0
- package/src/hooks/useFiles/src/useFiles.ts +35 -0
- package/src/hooks/useFiles/src/useSaveShareSettings.ts +31 -0
- package/src/imports.ts +19 -0
- package/src/index.ts +8 -1
- package/src/styles/{App.css → SecureFiles/App.css} +0 -0
- package/src/styles/{index.css → SecureFiles/index.css} +0 -0
- package/src/styles/SecureFiles/logs/clipit_logs.log +0 -0
- package/src/styles/SecureFiles/logs/routes_bp.log +0 -0
- package/src/styles/{tailwind.css → SecureFiles/tailwind.css} +0 -0
- package/src/styles/{window-manager.css → SecureFiles/window-manager.css} +0 -0
- package/src/styles/SecureFilesStyles.module.css +287 -0
- package/src/styles/index.ts +1 -0
- package/src/types/index.ts +1 -0
- package/src/types/src/index.ts +1 -0
- package/src/types/src/types.ts +88 -0
- package/tailwind.config.cjs +0 -0
- package/testit.mjs +0 -0
- package/testit.ts +0 -0
- package/tsconfig.json +0 -0
- package/.github/workflows/release.yml +0 -17
- package/dist/secure_files.d.ts +0 -4
- package/src/secure_files.tsx +0 -41
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type {QueuedFile, FileItem,FileTableProps,UploadProps,ShareModalProps,FileRowProps } from './../imports';
|
|
2
|
+
export {
|
|
3
|
+
CloudArrowUpIcon,
|
|
4
|
+
currentUsername,
|
|
5
|
+
Button,
|
|
6
|
+
Checkbox,
|
|
7
|
+
Input,
|
|
8
|
+
Spinner,
|
|
9
|
+
useEffect,
|
|
10
|
+
useRef,
|
|
11
|
+
useState,
|
|
12
|
+
useCallback,
|
|
13
|
+
ClipboardIcon,
|
|
14
|
+
createPortal,
|
|
15
|
+
fetchSharePatch,
|
|
16
|
+
} from './../imports';
|
|
17
|
+
export {useFiles} from './../hooks'
|
|
18
|
+
export { downloadFile, uploadFiles } from './../file_utils'
|
package/src/UI/index.tsx
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
export type String = string | undefined | null;
|
|
4
|
+
export type Astring = any[] | string | undefined | null;
|
|
5
|
+
export type Trailing = React.ReactNode
|
|
6
|
+
export type ImportElem = {
|
|
7
|
+
reaxlEle?:React.InputHTMLAttributes<HTMLInputElement>;
|
|
8
|
+
label?: String;
|
|
9
|
+
checked?:boolean;
|
|
10
|
+
type?:string;
|
|
11
|
+
onChange?: any;
|
|
12
|
+
value?: any;
|
|
13
|
+
trailing?:Trailing;
|
|
14
|
+
className?: String;
|
|
15
|
+
classNames?:Astring;
|
|
16
|
+
disabled?: any;
|
|
17
|
+
placeholder?: any;
|
|
18
|
+
error?: string;
|
|
19
|
+
min?:any,
|
|
20
|
+
readOnly?:boolean
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Utility to convert input to array
|
|
24
|
+
function ensure_list(obj: Astring | null | undefined): string[] {
|
|
25
|
+
if (Array.isArray(obj)) return obj.filter(Boolean);
|
|
26
|
+
if (typeof obj === 'string') return obj.split(' ').filter(Boolean);
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Get list and length safely
|
|
31
|
+
function getListAndLen(obj: Astring | null | undefined): { return_obj: string[]; ob_len: number } {
|
|
32
|
+
const return_obj = ensure_list(obj);
|
|
33
|
+
const ob_len = return_obj.length;
|
|
34
|
+
return { return_obj, ob_len };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Combine class names with defaults
|
|
38
|
+
function combineClassNames(classNames: Astring = [], defaultClasses: Astring = ['form-group', 'form-label', 'flex', 'gap-2']): string[] {
|
|
39
|
+
const { return_obj: inputClasses } = getListAndLen(classNames);
|
|
40
|
+
const { return_obj: defaultClassList } = getListAndLen(defaultClasses);
|
|
41
|
+
|
|
42
|
+
// Combine and remove duplicates
|
|
43
|
+
const combined = [...new Set([...defaultClassList, ...inputClasses])];
|
|
44
|
+
|
|
45
|
+
console.log(`Combined classes (length: ${combined.length}): ${combined.join(' ')}`);
|
|
46
|
+
return combined;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function Button({
|
|
50
|
+
children,
|
|
51
|
+
color = 'gray',
|
|
52
|
+
variant = 'default',
|
|
53
|
+
className = '',
|
|
54
|
+
...rest
|
|
55
|
+
}: React.ButtonHTMLAttributes<HTMLButtonElement> & {
|
|
56
|
+
color?: 'gray' | 'green' | 'blue';
|
|
57
|
+
variant?: 'default' | 'icon' | 'primary' | 'secondary';
|
|
58
|
+
className?: string;
|
|
59
|
+
}) {
|
|
60
|
+
const variantClasses = {
|
|
61
|
+
default: 'btn',
|
|
62
|
+
icon: 'btn-icon',
|
|
63
|
+
primary: 'btn-primary',
|
|
64
|
+
secondary: 'btn-secondary',
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const colorClasses = {
|
|
68
|
+
gray: '',
|
|
69
|
+
green: variant === 'primary' ? 'btn-success' : 'btn-secondary',
|
|
70
|
+
blue: variant === 'primary' ? 'btn-primary' : 'btn-secondary',
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const classes = combineClassNames([variantClasses[variant], colorClasses[color], className]);
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<button className={classes.join(' ')} {...rest}>
|
|
77
|
+
{children}
|
|
78
|
+
</button>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function Checkbox({
|
|
83
|
+
label,
|
|
84
|
+
className = '',
|
|
85
|
+
classNames = [],
|
|
86
|
+
...rest
|
|
87
|
+
}: ImportElem) {
|
|
88
|
+
const classes = combineClassNames(classNames, ['checkbox', 'wm-checkbox']);
|
|
89
|
+
|
|
90
|
+
return (
|
|
91
|
+
<label className={classes.join(' ')}>
|
|
92
|
+
<input type="checkbox" className="wm-checkbox" {...rest} />
|
|
93
|
+
{label && <span>{label}</span>}
|
|
94
|
+
</label>
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function Input({
|
|
99
|
+
label,
|
|
100
|
+
trailing,
|
|
101
|
+
className = '',
|
|
102
|
+
classNames = [],
|
|
103
|
+
...rest
|
|
104
|
+
}: ImportElem) {
|
|
105
|
+
const classes = combineClassNames(classNames, ['form-group', 'flex', 'gap-2']);
|
|
106
|
+
const inputClasses = combineClassNames(className, ['form-input']);
|
|
107
|
+
|
|
108
|
+
return (
|
|
109
|
+
<label className={classes.join(' ')}>
|
|
110
|
+
{label && <span className="form-label">{label}</span>}
|
|
111
|
+
<div className="flex gap-2">
|
|
112
|
+
<input className={inputClasses.join(' ')} {...rest} />
|
|
113
|
+
{trailing}
|
|
114
|
+
</div>
|
|
115
|
+
</label>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function Spinner() {
|
|
120
|
+
const classes = combineClassNames('animate-pulse');
|
|
121
|
+
return <p className={classes.join(' ')}>Loading…</p>;
|
|
122
|
+
}
|
package/src/custom.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './src';
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cookie-authenticated API helpers.
|
|
3
|
+
* All requests include credentials: 'include' so the browser sends the
|
|
4
|
+
* session cookie (auth_session) automatically. No JWT tokens are used.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const API_BASE = "/api/secure-files";
|
|
8
|
+
|
|
9
|
+
function apiUrl(path: string): string {
|
|
10
|
+
const p = path.startsWith("/") ? path : `/${path}`;
|
|
11
|
+
return `${API_BASE}${p}`;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Generic JSON fetch with session-cookie auth.
|
|
16
|
+
* Throws Error("UNAUTHENTICATED") on 401 so callers can redirect to login.
|
|
17
|
+
*/
|
|
18
|
+
export async function cookieFetch<T = unknown>(
|
|
19
|
+
path: string,
|
|
20
|
+
body?: unknown,
|
|
21
|
+
method?: "GET" | "POST" | "PUT" | "PATCH" | "DELETE",
|
|
22
|
+
): Promise<T> {
|
|
23
|
+
const m = method ?? (body !== undefined ? "POST" : "GET");
|
|
24
|
+
const init: RequestInit = { method: m, credentials: "include" };
|
|
25
|
+
|
|
26
|
+
if (body !== undefined && body !== null) {
|
|
27
|
+
init.headers = { "Content-Type": "application/json" };
|
|
28
|
+
init.body = JSON.stringify(body);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const res = await fetch(apiUrl(path), init);
|
|
32
|
+
if (res.status === 401) throw new Error("UNAUTHENTICATED");
|
|
33
|
+
if (!res.ok) {
|
|
34
|
+
const msg = await res.text().catch(() => res.statusText);
|
|
35
|
+
throw new Error(`HTTP ${res.status}: ${msg}`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const ct = res.headers.get("Content-Type") ?? "";
|
|
39
|
+
return (ct.includes("application/json") ? res.json() : res.text()) as Promise<T>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Check whether the current session is valid.
|
|
44
|
+
* Calls /userInfo which requires authentication; returns true if the user is
|
|
45
|
+
* logged in, false otherwise (no exception thrown).
|
|
46
|
+
*/
|
|
47
|
+
export async function checkAuth(): Promise<boolean> {
|
|
48
|
+
try {
|
|
49
|
+
const res = await fetch(apiUrl("/userInfo"), {
|
|
50
|
+
method: "GET",
|
|
51
|
+
credentials: "include",
|
|
52
|
+
});
|
|
53
|
+
return res.ok;
|
|
54
|
+
} catch {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Upload a single file using multipart/form-data with session-cookie auth.
|
|
61
|
+
* @param file The File object to upload.
|
|
62
|
+
* @param relPath Optional relative path within the user's directory.
|
|
63
|
+
*/
|
|
64
|
+
export async function cookieUpload(file: File, relPath = ""): Promise<unknown> {
|
|
65
|
+
const form = new FormData();
|
|
66
|
+
form.append("file", file);
|
|
67
|
+
if (relPath) form.append("rel_path", relPath);
|
|
68
|
+
|
|
69
|
+
const res = await fetch(apiUrl("/upload"), {
|
|
70
|
+
method: "POST",
|
|
71
|
+
credentials: "include",
|
|
72
|
+
body: form,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
if (res.status === 401) throw new Error("UNAUTHENTICATED");
|
|
76
|
+
if (!res.ok) throw new Error(`Upload failed: HTTP ${res.status}`);
|
|
77
|
+
return res.json();
|
|
78
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { secureFetchIt,requireToken,alertit } from "./../imports";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './src';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
|
|
2
|
+
import {removeFile} from './delete_select'
|
|
3
|
+
import {alertit} from './../imports'
|
|
4
|
+
/** Given a checkbox inside a table-row → download that row’s file. */
|
|
5
|
+
export async function removeRow(cb: HTMLInputElement): Promise<void> {
|
|
6
|
+
const row = cb.closest("tr") as HTMLTableRowElement | null;
|
|
7
|
+
alertit(row)
|
|
8
|
+
if (row && row?.id) await removeFile(row);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { cookieFetch } from '../../cookieFetch';
|
|
2
|
+
|
|
3
|
+
export async function removeFile(
|
|
4
|
+
item: { id?: number | string; filename?: string },
|
|
5
|
+
): Promise<void> {
|
|
6
|
+
const resp = await cookieFetch<unknown>("remove", { id: item.id }, "POST");
|
|
7
|
+
if (!resp || (resp as any) === false || (resp as any)?.result === false) {
|
|
8
|
+
throw new Error(`Could not delete "${item.filename ?? item.id}"`);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {removeFile} from './delete_select';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
/** Bulk download: pass a NodeList or array of checkboxes. */
|
|
5
|
+
export async function removeSelected(checkboxes: Iterable<HTMLInputElement>): Promise<void> {
|
|
6
|
+
const list = Array.from(checkboxes);
|
|
7
|
+
if (!list.length) {
|
|
8
|
+
alert("No files selected.");
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
for (const cb of list) {
|
|
12
|
+
alert(cb);
|
|
13
|
+
await removeFile(cb);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './src';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import {triggerDownload} from './trigger_download';
|
|
2
|
+
import {FileItem} from './../imports';
|
|
3
|
+
/** Non-React “direct download” used by some old links (kept for parity) */
|
|
4
|
+
export async function directDownload(item: FileItem): Promise<void> {
|
|
5
|
+
const token = localStorage.getItem("token");
|
|
6
|
+
if (!token) throw new Error("No auth token found");
|
|
7
|
+
|
|
8
|
+
const res = await fetch("https://abstractendeavors.com/api/secure-files/secure-download/", {
|
|
9
|
+
method: "POST",
|
|
10
|
+
headers: {
|
|
11
|
+
Authorization: `Bearer ${token}`,
|
|
12
|
+
"Content-Type": "application/json",
|
|
13
|
+
Accept: "application/octet-stream",
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
body: JSON.stringify(item),
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
if (!res.ok) throw new Error(`Download failed: ${res.status}`);
|
|
20
|
+
const blob = await res.blob();
|
|
21
|
+
triggerDownload(blob, item.filename);
|
|
22
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { FileItem } from './../imports';
|
|
2
|
+
import { triggerDownload } from './trigger_download';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Download a file using the current session cookie.
|
|
6
|
+
* No JWT / localStorage token required.
|
|
7
|
+
*/
|
|
8
|
+
export async function downloadFile(item: FileItem): Promise<void> {
|
|
9
|
+
const res = await fetch("/api/secure-files/download", {
|
|
10
|
+
method: "POST",
|
|
11
|
+
credentials: "include",
|
|
12
|
+
headers: { "Content-Type": "application/json" },
|
|
13
|
+
body: JSON.stringify({
|
|
14
|
+
id: item.id,
|
|
15
|
+
filepath: item.filepath,
|
|
16
|
+
filename: item.filename,
|
|
17
|
+
}),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
if (res.status === 401) throw new Error("Not authenticated – please log in.");
|
|
21
|
+
if (!res.ok) throw new Error(`Download failed: HTTP ${res.status}`);
|
|
22
|
+
|
|
23
|
+
const blob = await res.blob();
|
|
24
|
+
triggerDownload(blob, item.filename || "download");
|
|
25
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {requireToken,secureFetchIt,FileItem} from './../imports';
|
|
2
|
+
import {triggerDownload} from './trigger_download';
|
|
3
|
+
export async function downloadSelect(item: FileItem): Promise<void> {
|
|
4
|
+
const token: string = requireToken();
|
|
5
|
+
const body = item
|
|
6
|
+
const headers = {
|
|
7
|
+
Authorization: `Bearer ${token}`,
|
|
8
|
+
Accept: "application/octet-stream",
|
|
9
|
+
"Content-Type": "application/json"
|
|
10
|
+
}
|
|
11
|
+
const blob = await secureFetchIt( "download", body, "POST", headers, true, true) as Blob;
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
if (!blob || blob.size === 0) {
|
|
16
|
+
throw new Error(`Received empty file data for “${item.filename}”`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
triggerDownload(blob, item.filename);
|
|
20
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {downloadSelect} from './download_select';
|
|
2
|
+
import type {FileItem} from './../imports'
|
|
3
|
+
/** Given a checkbox inside a table-row → download that row’s file. */
|
|
4
|
+
export async function downloadRow(cb: HTMLInputElement): Promise<void> {
|
|
5
|
+
const row = cb.closest("tr") as HTMLTableRowElement | null;
|
|
6
|
+
const filepath = cb.value;
|
|
7
|
+
const filename = row?.children[1]?.textContent?.trim() || "downloaded_file";
|
|
8
|
+
if (filepath) await downloadSelect({filepath,filename} as any);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Bulk download: pass a NodeList or array of checkboxes. */
|
|
12
|
+
export async function downloadSelected(checkboxes: Iterable<HTMLInputElement>): Promise<void> {
|
|
13
|
+
const list = Array.from(checkboxes);
|
|
14
|
+
if (!list.length) {
|
|
15
|
+
alert("No files selected.");
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
for (const cb of list) {
|
|
19
|
+
await downloadRow(cb);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Helper function to trigger file download */
|
|
2
|
+
export function triggerDownload(blob: Blob, filename: string): void {
|
|
3
|
+
const url = URL.createObjectURL(blob);
|
|
4
|
+
const a = Object.assign(document.createElement("a"), {
|
|
5
|
+
href: url,
|
|
6
|
+
download: filename,
|
|
7
|
+
});
|
|
8
|
+
document.body.appendChild(a);
|
|
9
|
+
a.click();
|
|
10
|
+
a.remove();
|
|
11
|
+
URL.revokeObjectURL(url);
|
|
12
|
+
|
|
13
|
+
console.log(`✅ Successfully downloaded: ${filename}`);
|
|
14
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './src';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './list_utils';
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { cookieFetch } from './../../cookieFetch';
|
|
2
|
+
import type { ApiRow, FileItem } from './../imports';
|
|
3
|
+
|
|
4
|
+
export async function listFiles(
|
|
5
|
+
setFiles: ((rows: FileItem[]) => void) | null = null,
|
|
6
|
+
): Promise<FileItem[]> {
|
|
7
|
+
const result = await cookieFetch<ApiRow[] | { result: ApiRow[] }>("list", undefined, "GET");
|
|
8
|
+
const apiRows: ApiRow[] = Array.isArray(result)
|
|
9
|
+
? result
|
|
10
|
+
: ((result as any)?.result ?? []);
|
|
11
|
+
|
|
12
|
+
const rows: FileItem[] = apiRows.map((r: ApiRow) => ({
|
|
13
|
+
id: r.id,
|
|
14
|
+
filename: r.filename,
|
|
15
|
+
uploader_id: r.uploader_id,
|
|
16
|
+
filepath: r.filepath,
|
|
17
|
+
created_at: r.created_at,
|
|
18
|
+
download_count: r.download_count,
|
|
19
|
+
download_limit: r.download_limit,
|
|
20
|
+
shareable: Boolean(r.shareable),
|
|
21
|
+
needsPassword: !!r.share_password,
|
|
22
|
+
share_password: r.share_password,
|
|
23
|
+
password_str: r.password_str,
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
if (setFiles) setFiles(rows);
|
|
27
|
+
return rows;
|
|
28
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './src';
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import {syncHeaderCheckbox} from './sync_header_checkbox';
|
|
2
|
+
/** Wire up header + row checkboxes once the table is in the DOM. */
|
|
3
|
+
export function initSelectAll(): void {
|
|
4
|
+
const header = document.getElementById(
|
|
5
|
+
"select-all-checkbox"
|
|
6
|
+
) as HTMLInputElement;
|
|
7
|
+
const btn = document.getElementById("select-all") as HTMLButtonElement;
|
|
8
|
+
|
|
9
|
+
// 1) header → toggle all
|
|
10
|
+
header.addEventListener("change", (e) => {
|
|
11
|
+
const on = (e.target as HTMLInputElement).checked;
|
|
12
|
+
document
|
|
13
|
+
.querySelectorAll<HTMLInputElement>(".file-checkbox")
|
|
14
|
+
.forEach((cb) => (cb.checked = on));
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// 2) button → toggle all
|
|
18
|
+
btn.addEventListener("click", () => {
|
|
19
|
+
const cbs = Array.from(
|
|
20
|
+
document.querySelectorAll<HTMLInputElement>(".file-checkbox")
|
|
21
|
+
);
|
|
22
|
+
if (!cbs.length) {
|
|
23
|
+
alert("No files to select.");
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const anyUnchecked = cbs.some((cb) => !cb.checked);
|
|
27
|
+
cbs.forEach((cb) => (cb.checked = anyUnchecked));
|
|
28
|
+
syncHeaderCheckbox();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// 3) row boxes → keep header state
|
|
32
|
+
document
|
|
33
|
+
.querySelectorAll<HTMLInputElement>(".file-checkbox")
|
|
34
|
+
.forEach((cb) => cb.addEventListener("change", syncHeaderCheckbox));
|
|
35
|
+
|
|
36
|
+
syncHeaderCheckbox();
|
|
37
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Classic “select all rows” logic (still handy in non-React views
|
|
3
|
+
* such as the admin dashboard).
|
|
4
|
+
*/
|
|
5
|
+
export function syncHeaderCheckbox(): void {
|
|
6
|
+
const all = document.querySelectorAll<HTMLInputElement>(".file-checkbox");
|
|
7
|
+
const checked = document.querySelectorAll<HTMLInputElement>(
|
|
8
|
+
".file-checkbox:checked"
|
|
9
|
+
);
|
|
10
|
+
const header = document.getElementById(
|
|
11
|
+
"select-all-checkbox"
|
|
12
|
+
) as HTMLInputElement;
|
|
13
|
+
|
|
14
|
+
header.indeterminate =
|
|
15
|
+
checked.length > 0 && checked.length < all.length;
|
|
16
|
+
header.checked = checked.length === all.length;
|
|
17
|
+
}
|
|
18
|
+
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import {syncHeaderCheckbox} from './sync_header_checkbox';
|
|
2
|
+
/** Utility for other screens: toggle a list of checkboxes on/off. */
|
|
3
|
+
export function toggleCheckboxes(cbs: HTMLInputElement[]): void {
|
|
4
|
+
if (!cbs.length) {
|
|
5
|
+
alert("No files to select.");
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
const anyUnchecked = cbs.some((cb) => !cb.checked);
|
|
9
|
+
cbs.forEach((cb) => (cb.checked = anyUnchecked));
|
|
10
|
+
syncHeaderCheckbox();
|
|
11
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './src';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './share_utils';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { cookieFetch } from './../../cookieFetch';
|
|
2
|
+
import type { FileItem } from './../imports';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Toggle sharing on a file.
|
|
6
|
+
* Sends the updated settings to /api/secure-files/files/share.
|
|
7
|
+
* Does NOT update local state – the caller is responsible for that.
|
|
8
|
+
*/
|
|
9
|
+
export async function toggleShare(file: FileItem): Promise<void> {
|
|
10
|
+
const updated: FileItem = { ...file, shareable: !file.shareable };
|
|
11
|
+
if (!updated.shareable) updated.needsPassword = false;
|
|
12
|
+
|
|
13
|
+
await cookieFetch("files/share", {
|
|
14
|
+
id: updated.id,
|
|
15
|
+
shareable: updated.shareable,
|
|
16
|
+
share_password: updated.share_password ?? null,
|
|
17
|
+
password_str: updated.password_str ?? null,
|
|
18
|
+
download_limit: updated.download_limit ?? null,
|
|
19
|
+
}, "POST");
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
// No legacy auth imports needed — upload_file.ts uses cookieUpload directly.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './src';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const API_BASE = '/api/secure-files';
|
|
2
|
+
|
|
3
|
+
/** Generic PATCH helper for file-level admin settings. */
|
|
4
|
+
export async function patchFileSetting(
|
|
5
|
+
fileId: string,
|
|
6
|
+
field: string,
|
|
7
|
+
value: unknown,
|
|
8
|
+
): Promise<Record<string, unknown>> {
|
|
9
|
+
const res = await fetch(`${API_BASE}/files/${fileId}/settings/${field}`, {
|
|
10
|
+
method: 'PATCH',
|
|
11
|
+
credentials: 'include',
|
|
12
|
+
headers: { 'Content-Type': 'application/json' },
|
|
13
|
+
body: JSON.stringify({ value }),
|
|
14
|
+
});
|
|
15
|
+
if (!res.ok) {
|
|
16
|
+
const err = await res.json().catch(() => ({}));
|
|
17
|
+
throw new Error((err as any).error || res.statusText);
|
|
18
|
+
}
|
|
19
|
+
return (await res.json()) as Record<string, unknown>;
|
|
20
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { cookieUpload } from './../../cookieFetch';
|
|
2
|
+
|
|
3
|
+
/** File-upload helper (used by non-React fallback pages). */
|
|
4
|
+
export async function uploadFile(file: any, subdir: any): Promise<any> {
|
|
5
|
+
const fileInput = file || document.getElementById('file-browser') as HTMLInputElement;
|
|
6
|
+
const subdirInput = subdir || document.getElementById('subdir') as HTMLInputElement;
|
|
7
|
+
const banner = document.getElementById('banner') as HTMLElement;
|
|
8
|
+
|
|
9
|
+
if (!fileInput.files?.length) {
|
|
10
|
+
alert('No file selected.');
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const f = fileInput.files[0];
|
|
15
|
+
const dir = (subdirInput?.value ?? '').trim();
|
|
16
|
+
const relPath = dir ? `${dir}/${f.name}` : f.name;
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
const json = await cookieUpload(f, relPath) as { filepath: string };
|
|
20
|
+
if (banner) banner.textContent = `Uploaded: ${json.filepath}`;
|
|
21
|
+
fileInput.value = '';
|
|
22
|
+
if (subdirInput) subdirInput.value = '';
|
|
23
|
+
window.location.reload();
|
|
24
|
+
} catch (err: any) {
|
|
25
|
+
console.error('Upload failed', err);
|
|
26
|
+
}
|
|
27
|
+
}
|