Skip to content

Commit 4f931ab

Browse files
nezajdwwoelfel
andauthored
༼ つ ◕_◕ ༽つ Give Storage (#733)
Co-authored-by: Daniel Woelfel <[email protected]>
1 parent a79782c commit 4f931ab

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1796
-1466
lines changed

client/packages/admin/src/index.ts

+101-50
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ import {
4747
type InstantRules,
4848
type UpdateParams,
4949
type LinkParams,
50+
51+
// storage types
52+
type FileOpts,
53+
type UploadFileResponse,
54+
type DeleteFileResponse,
5055
} from "@instantdb/core";
5156

5257
import version from "./version";
@@ -283,8 +288,8 @@ class InstantAdmin<
283288
*/
284289
query = <
285290
Q extends Schema extends InstantGraph<any, any>
286-
? InstaQLParams<Schema>
287-
: Exactly<Query, Q>,
291+
? InstaQLParams<Schema>
292+
: Exactly<Query, Q>,
288293
>(
289294
query: Q,
290295
): Promise<QueryResponse<Q, Schema, WithCardinalityInference>> => {
@@ -592,7 +597,6 @@ class Auth {
592597
}
593598
}
594599

595-
type UploadMetadata = { contentType?: string } & Record<string, any>;
596600
type StorageFile = {
597601
key: string;
598602
name: string;
@@ -601,6 +605,12 @@ type StorageFile = {
601605
last_modified: number;
602606
};
603607

608+
type DeleteManyFileResponse = {
609+
data: {
610+
ids: string[] | null;
611+
};
612+
};
613+
604614
/**
605615
* Functions to manage file storage.
606616
*/
@@ -617,12 +627,73 @@ class Storage {
617627
* @see https://instantdb.com/docs/storage
618628
* @example
619629
* const buffer = fs.readFileSync('demo.png');
620-
* const isSuccess = await db.storage.upload('photos/demo.png', buffer);
630+
* const isSuccess = await db.storage.uploadFile('photos/demo.png', buffer);
631+
*/
632+
uploadFile = async (
633+
path: string,
634+
file: Buffer,
635+
metadata: FileOpts = {},
636+
): Promise<UploadFileResponse> => {
637+
const headers = {
638+
...authorizedHeaders(this.config),
639+
path,
640+
"content-type": metadata.contentType || "application/octet-stream",
641+
};
642+
if (metadata.contentDisposition) {
643+
headers["content-disposition"] = metadata.contentDisposition;
644+
}
645+
646+
const data = await jsonFetch(`${this.config.apiURI}/admin/storage/upload`, {
647+
method: "PUT",
648+
headers,
649+
body: file,
650+
});
651+
652+
return data;
653+
};
654+
655+
/**
656+
* Deletes a file by its path name (e.g. "photos/demo.png").
657+
*
658+
* @see https://instantdb.com/docs/storage
659+
* @example
660+
* await db.storage.delete("photos/demo.png");
661+
*/
662+
delete = async (pathname: string): Promise<DeleteFileResponse> => {
663+
return jsonFetch(
664+
`${this.config.apiURI}/admin/storage/files?filename=${encodeURIComponent(
665+
pathname,
666+
)}`,
667+
{
668+
method: "DELETE",
669+
headers: authorizedHeaders(this.config),
670+
},
671+
);
672+
};
673+
674+
/**
675+
* Deletes multiple files by their path names (e.g. "photos/demo.png", "essays/demo.txt").
676+
*
677+
* @see https://instantdb.com/docs/storage
678+
* @example
679+
* await db.storage.deleteMany(["images/1.png", "images/2.png", "images/3.png"]);
680+
*/
681+
deleteMany = async (pathnames: string[]): Promise<DeleteManyFileResponse> => {
682+
return jsonFetch(`${this.config.apiURI}/admin/storage/files/delete`, {
683+
method: "POST",
684+
headers: authorizedHeaders(this.config),
685+
body: JSON.stringify({ filenames: pathnames }),
686+
});
687+
};
688+
689+
/**
690+
* @deprecated. This method will be removed in the future. Use `uploadFile`
691+
* instead
621692
*/
622693
upload = async (
623694
pathname: string,
624695
file: Buffer,
625-
metadata: UploadMetadata = {},
696+
metadata: FileOpts = {},
626697
): Promise<boolean> => {
627698
const { data: presignedUrl } = await jsonFetch(
628699
`${this.config.apiURI}/admin/storage/signed-upload-url`,
@@ -647,15 +718,13 @@ class Storage {
647718
};
648719

649720
/**
650-
* Retrieves a download URL for the provided path.
651-
*
652-
* @see https://instantdb.com/docs/storage
721+
* @deprecated. This method will be removed in the future. Use `query` instead
653722
* @example
654-
* const url = await db.storage.getDownloadUrl('photos/demo.png');
723+
* const files = await db.query({ $files: {}})
655724
*/
656-
getDownloadUrl = async (pathname: string): Promise<string> => {
725+
list = async (): Promise<StorageFile[]> => {
657726
const { data } = await jsonFetch(
658-
`${this.config.apiURI}/admin/storage/signed-download-url?app_id=${this.config.appId}&filename=${encodeURIComponent(pathname)}`,
727+
`${this.config.apiURI}/admin/storage/files`,
659728
{
660729
method: "GET",
661730
headers: authorizedHeaders(this.config),
@@ -665,16 +734,24 @@ class Storage {
665734
return data;
666735
};
667736

737+
668738
/**
669-
* Retrieves a list of all the files that have been uploaded by this app.
670-
*
671-
* @see https://instantdb.com/docs/storage
672-
* @example
673-
* const files = await db.storage.list();
739+
* @deprecated. getDownloadUrl will be removed in the future.
740+
* Use `query` instead to query and fetch for valid urls
741+
*
742+
* db.useQuery({
743+
* $files: {
744+
* $: {
745+
* where: {
746+
* path: "moop.png"
747+
* }
748+
* }
749+
* }
750+
* })
674751
*/
675-
list = async (): Promise<StorageFile[]> => {
752+
getDownloadUrl = async (pathname: string): Promise<string> => {
676753
const { data } = await jsonFetch(
677-
`${this.config.apiURI}/admin/storage/files`,
754+
`${this.config.apiURI}/admin/storage/signed-download-url?app_id=${this.config.appId}&filename=${encodeURIComponent(pathname)}`,
678755
{
679756
method: "GET",
680757
headers: authorizedHeaders(this.config),
@@ -683,38 +760,6 @@ class Storage {
683760

684761
return data;
685762
};
686-
687-
/**
688-
* Deletes a file by its path name (e.g. "photos/demo.png").
689-
*
690-
* @see https://instantdb.com/docs/storage
691-
* @example
692-
* await db.storage.delete("photos/demo.png");
693-
*/
694-
delete = async (pathname: string): Promise<void> => {
695-
await jsonFetch(
696-
`${this.config.apiURI}/admin/storage/files?filename=${encodeURIComponent(pathname)}`,
697-
{
698-
method: "DELETE",
699-
headers: authorizedHeaders(this.config),
700-
},
701-
);
702-
};
703-
704-
/**
705-
* Deletes multiple files by their path names (e.g. "photos/demo.png", "essays/demo.txt").
706-
*
707-
* @see https://instantdb.com/docs/storage
708-
* @example
709-
* await db.storage.deleteMany(["images/1.png", "images/2.png", "images/3.png"]);
710-
*/
711-
deleteMany = async (pathnames: string[]): Promise<void> => {
712-
await jsonFetch(`${this.config.apiURI}/admin/storage/files/delete`, {
713-
method: "POST",
714-
headers: authorizedHeaders(this.config),
715-
body: JSON.stringify({ filenames: pathnames }),
716-
});
717-
};
718763
}
719764

720765
/**
@@ -955,4 +1000,10 @@ export {
9551000
type InstantRules,
9561001
type UpdateParams,
9571002
type LinkParams,
1003+
1004+
// storage types
1005+
type FileOpts,
1006+
type UploadFileResponse,
1007+
type DeleteFileResponse,
1008+
type DeleteManyFileResponse,
9581009
};

client/packages/core/src/Reactor.js

+30-13
Original file line numberDiff line numberDiff line change
@@ -1497,7 +1497,7 @@ export default class Reactor {
14971497
appId: this.config.appId,
14981498
refreshToken,
14991499
});
1500-
} catch (e) {}
1500+
} catch (e) { }
15011501
}
15021502
await this.changeCurrentUser(null);
15031503
}
@@ -1796,6 +1796,35 @@ export default class Reactor {
17961796
// --------
17971797
// Storage
17981798

1799+
async uploadFile(path, file, opts) {
1800+
const currentUser = await this.getCurrentUser();
1801+
const refreshToken = currentUser?.user?.refresh_token;
1802+
return StorageApi.uploadFile({
1803+
...opts,
1804+
apiURI: this.config.apiURI,
1805+
appId: this.config.appId,
1806+
path: path,
1807+
file,
1808+
refreshToken: refreshToken,
1809+
});
1810+
}
1811+
1812+
async deleteFile(path) {
1813+
const currentUser = await this.getCurrentUser();
1814+
const refreshToken = currentUser?.user?.refresh_token;
1815+
const result = await StorageApi.deleteFile({
1816+
apiURI: this.config.apiURI,
1817+
appId: this.config.appId,
1818+
path,
1819+
refreshToken: refreshToken,
1820+
});
1821+
1822+
return result;
1823+
}
1824+
1825+
// Deprecated Storage API (Jan 2025)
1826+
// ---------------------------------
1827+
17991828
async upload(path, file) {
18001829
const currentUser = await this.getCurrentUser();
18011830
const refreshToken = currentUser?.user?.refresh_token;
@@ -1824,16 +1853,4 @@ export default class Reactor {
18241853
return url;
18251854
}
18261855

1827-
async deleteFile(path) {
1828-
const currentUser = await this.getCurrentUser();
1829-
const refreshToken = currentUser?.user?.refresh_token;
1830-
const result = await StorageApi.deleteFile({
1831-
apiURI: this.config.apiURI,
1832-
appId: this.config.appId,
1833-
path: path,
1834-
refreshToken: refreshToken,
1835-
});
1836-
1837-
return result;
1838-
}
18391856
}

0 commit comments

Comments
 (0)