Skip to content
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

[WIP] ༼ つ ◕_◕ ༽つ Give Storage #733

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
22 changes: 22 additions & 0 deletions client/packages/admin/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,28 @@ class Storage {
return ok;
};

upload2 = async (
filename: string,
file: Buffer,
metadata: UploadMetadata = {},
): Promise<any> => {
const formData = new FormData();
formData.append('path', filename);
formData.append('file', new Blob([file]));

const headers = authorizedHeaders(this.config)
delete headers["content-type"];

const data = await jsonFetch(`${this.config.apiURI}/admin/storage/upload`, {
method: "POST",
headers,
body: formData
})

return data;
};


/**
* Retrieves a download URL for the provided path.
*
Expand Down
46 changes: 36 additions & 10 deletions client/packages/core/src/Reactor.js
Original file line number Diff line number Diff line change
Expand Up @@ -432,13 +432,13 @@ export default class Reactor {
break;
}

// Now that this transaction is accepted,
// We can delete it from our queue.
// Now that this transaction is accepted,
// We can delete it from our queue.
this.pendingMutations.set((prev) => {
prev.delete(eventId);
return prev;
});

// We apply this transaction to all our existing queries
const txStepsToApply = prevMutation["tx-steps"];
this.querySubs.set((prev) => {
Expand All @@ -457,9 +457,9 @@ export default class Reactor {
.filter(([action, ..._args]) => action === "add-attr")
.map(([_action, attr]) => attr)
.concat(Object.values(this.attrs));

this._setAttrs(newAttrs);

this._finishTransaction("synced", eventId);
break;
case "patch-presence": {
Expand Down Expand Up @@ -1494,7 +1494,7 @@ export default class Reactor {
appId: this.config.appId,
refreshToken,
});
} catch (e) {}
} catch (e) { }
}
await this.changeCurrentUser(null);
}
Expand Down Expand Up @@ -1787,18 +1787,44 @@ export default class Reactor {
async upload(path, file) {
const currentUser = await this.getCurrentUser();
const refreshToken = currentUser?.user?.refresh_token;
const fileName = path || file.name;
const url = await StorageApi.getSignedUploadUrl({
const fileName = (path || file.name).replace(':', '/');
const { url, file_id } = await StorageApi.getSignedUploadUrl({
apiURI: this.config.apiURI,
appId: this.config.appId,
fileName: fileName,
fileName,
refreshToken: refreshToken,
});
const isSuccess = await StorageApi.upload(url, file);

await StorageApi.markUploadReady({
apiURI: this.config.apiURI,
appId: this.config.appId,
fileName,
refreshToken: refreshToken,
});

return isSuccess;
}

async upload2(path, file) {
const currentUser = await this.getCurrentUser();
const refreshToken = currentUser?.user?.refresh_token;
// (XXX): Remove replace code, this is temporary for testing s3 paths
const cleanPath = path.replace(':', '/');
return StorageApi.upload2({
apiURI: this.config.apiURI,
appId: this.config.appId,
path: cleanPath,
file,
refreshToken: refreshToken,
});
}


/**
* @deprecated Use loadUrl() instead. getDownloadUrl() will be removed in the
* future.
*/
async getDownloadUrl(path) {
const currentUser = await this.getCurrentUser();
const refreshToken = currentUser?.user?.refresh_token;
Expand All @@ -1818,7 +1844,7 @@ export default class Reactor {
const result = await StorageApi.deleteFile({
apiURI: this.config.apiURI,
appId: this.config.appId,
path: path,
path,
refreshToken: refreshToken,
});

Expand Down
58 changes: 57 additions & 1 deletion client/packages/core/src/StorageAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export async function getSignedUploadUrl({
refreshToken?: string;
metadata?: Record<string, any>;
}) {
const { data } = await jsonFetch(`${apiURI}/storage/signed-upload-url`, {
const data = await jsonFetch(`${apiURI}/storage/signed-upload-url`, {
method: "POST",
headers: {
"content-type": "application/json",
Expand All @@ -40,6 +40,62 @@ export async function upload(presignedUrl, file) {
return response.ok;
}

export async function upload2({
apiURI,
appId,
path,
file,
refreshToken,
}: {
apiURI: string;
appId: string;
path: string;
file: File;
refreshToken?: string;
}) {
const formData = new FormData();
formData.append('app_id', appId);
formData.append('path', path);
formData.append('file', file);

const data = await jsonFetch(`${apiURI}/storage/upload`, {
method: "POST",
headers: {
authorization: `Bearer ${refreshToken}`,
},
body: formData,
});

return data;
}


export async function markUploadReady({
apiURI,
appId,
fileName,
refreshToken,
}: {
apiURI: string;
appId: string;
fileName: string;
refreshToken?: string;
}) {
const { data } = await jsonFetch(`${apiURI}/storage/mark-upload-ready`, {
method: "POST",
headers: {
"content-type": "application/json",
authorization: `Bearer ${refreshToken}`,
},
body: JSON.stringify({
app_id: appId,
filename: fileName,
}),
});

return data;
}

export async function getDownloadUrl({
apiURI,
appId,
Expand Down
12 changes: 7 additions & 5 deletions client/packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -332,17 +332,18 @@ class Storage {
return this.db.upload(pathname, file);
};

upload2 = (pathname: string, file: File) => {
return this.db.upload2(pathname, file);
};


/**
* @deprecated Use `db.storage.upload` instead
*/
put = this.upload;

/**
* Retrieves a download URL for the provided path.
*
* @see https://instantdb.com/docs/storage
* @example
* const url = await db.storage.getDownloadUrl('photos/demo.png');
* @deprecated Use `db.storage.loadUrl` instead
*/
getDownloadUrl = (pathname: string) => {
return this.db.getDownloadUrl(pathname);
Expand All @@ -358,6 +359,7 @@ class Storage {
delete = (pathname: string) => {
return this.db.deleteFile(pathname);
};

}

// util
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
116 changes: 107 additions & 9 deletions client/sandbox/admin-sdk-express/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,23 @@ import { init, tx, id } from "@instantdb/admin";
import { assert } from "console";
import dotenv from "dotenv";
import fs from "fs";
import path from "path";

dotenv.config();

// const config = {
// apiURI: "http://localhost:8888",
// appId: process.env.INSTANT_APP_ID!,
// adminToken: process.env.INSTANT_ADMIN_TOKEN!,
// };

const config = {
apiURI: "http://localhost:8888",
appId: process.env.INSTANT_APP_ID!,
adminToken: process.env.INSTANT_ADMIN_TOKEN!,
appId: "831355ee-6a59-4990-8ef3-9c9fe7c26031",
adminToken: "0e91326c-b05a-4c0c-b50a-229ae49f301f",
};


const PERSONAL_ACCESS_TOKEN = process.env.INSTANT_PERSONAL_ACCESS_TOKEN!;

const db = init(config);
Expand Down Expand Up @@ -133,6 +141,17 @@ async function testDeleteUser() {
}
}

// testCreateToken();
// testQuery();
// testTransact();
// testScoped();
// testSignOut();
// testFetchUser();
// testDeleteUser();

/**
* Admin
*/
async function testAdminStorage(
src: string,
dest: string,
Expand Down Expand Up @@ -167,18 +186,97 @@ async function testAdminStorageBulkDelete(keyword: string) {
console.log("After:", await db.storage.list());
}

// testCreateToken();
// testQuery();
// testTransact();
// testScoped();
// testSignOut();
// testFetchUser();
// testDeleteUser();
async function testUploadFile(
src: string,
dest: string,
contentType?: string,
) {
const buffer = fs.readFileSync(path.join(__dirname, src));
const data = await db.storage.upload2(dest, buffer, {
contentType: contentType,
});
console.log("Uploaded:", data);
}

async function testQueryFiles() {
const res = await query({ $files: {} });
console.log(JSON.stringify(res, null, 2));
}

async function testDeleteSingleFile(filepath: string) {
console.log("Before:", await db.storage.list());
await db.storage.delete(filepath);
console.log("After:", await db.storage.list());
}

async function testDeleteBulkFile(filenames: string[]) {
console.log("Before:", await db.storage.list());
await db.storage.deleteMany(filenames);
console.log("After:", await db.storage.list());
}

async function testUpdateFileFails() {
const fileId = "cbda1941-d192-4f7d-b0a7-f9d428e1ca0b"
const prefix = "Update on $files"
const message = `${prefix} should not be supported`
try {
await transact(tx.$files[fileId].update({ metadata: { "new": "first" } }))
throw new Error(message)
} catch (err) {
if (err instanceof Error && err.message === message) {
throw err
} else {
console.log(`${prefix} failed as expected!`)
}
}
}

async function testMergeFileFails() {
const fileId = "cbda1941-d192-4f7d-b0a7-f9d428e1ca0b"
const prefix = "Merge on $files"
const message = `${prefix} should not be supported`
try {
await transact(tx.$files[fileId].merge({ metadata: { "new": "second" } }))
throw new Error(message)
} catch (err) {
if (err instanceof Error && err.message === message) {
throw err
} else {
console.log(`${prefix} failed as expected!`)
}
}
}

async function testDeleteFileTransactFails() {
const prefix = "Delete on $files"
const message = `${prefix} should not be supported`
try {
await transact(tx["$files"][id()].delete())
throw new Error(message)
} catch (err) {
if (err instanceof Error && err.message === message) {
throw err
} else {
console.log(`${prefix} failed as expected!`)
}
}
}

// (XXX): Rename these once we validate backwards compatibility
// testAdminStorage("src/demo.jpeg", "admin/demo.jpeg", "image/jpeg");
// testAdminStorageFiles();
// testAdminStorageDelete("admin/demo.jpeg");
// testAdminStorageBulkDelete("admin/demo");

// testUploadFile("circle_blue.jpg", "circle_blue.jpg", "image/jpeg");
// testUploadFile("circle_blue.jpg", "circle_blue2.jpg", "image/jpeg");
// testQueryFiles()
// testDeleteSingleFile("circle_blue.jpg");
// testDeleteBulkFile(["circle_blue.jpg", "circle_blue2.jpg"]);
// testUpdateFileFails()
// testMergeFileFails()
// testDeleteFileTransactFails()

/**
* Superadmin
*/
Expand Down
Binary file added client/sandbox/react-nextjs/imgs/circle_blue.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added client/sandbox/react-nextjs/imgs/circle_green.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added client/sandbox/react-nextjs/imgs/circle_red.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added client/sandbox/react-nextjs/imgs/square_blue.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added client/sandbox/react-nextjs/imgs/square_green.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added client/sandbox/react-nextjs/imgs/square_red.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added client/sandbox/react-nextjs/imgs/triangle_red.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading