supports custom resource URIs for two upload endpoints

This commit is contained in:
@wwwjim 2020-10-23 03:34:31 -07:00
parent 9713a838a9
commit 15fa57c2df
10 changed files with 87 additions and 104 deletions

View File

@ -1,6 +1,7 @@
import * as Actions from "~/common/actions"; import * as Actions from "~/common/actions";
import * as Store from "~/common/store"; import * as Store from "~/common/store";
import * as Constants from "~/common/constants"; import * as Constants from "~/common/constants";
import * as Credentials from "~/common/credentials";
import { dispatchCustomEvent } from "~/common/custom-events"; import { dispatchCustomEvent } from "~/common/custom-events";
import { encode } from "blurhash"; import { encode } from "blurhash";
@ -31,7 +32,13 @@ const encodeImageToBlurhash = async (imageUrl) => {
return encode(imageData.data, imageData.width, imageData.height, 4, 4); return encode(imageData.data, imageData.width, imageData.height, 4, 4);
}; };
export const upload = async ({ file, context, bucketName }) => { // NOTE(jim): We're speaking to a different server now.
const getCookie = (name) => {
var match = document.cookie.match(new RegExp("(^| )" + name + "=([^;]+)"));
if (match) return match[2];
};
export const upload = async ({ file, context, bucketName, routes }) => {
let formData = new FormData(); let formData = new FormData();
const HEIC2ANY = require("heic2any"); const HEIC2ANY = require("heic2any");
@ -61,14 +68,13 @@ export const upload = async ({ file, context, bucketName }) => {
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
const XHR = new XMLHttpRequest(); const XHR = new XMLHttpRequest();
window.addEventListener( window.addEventListener(`cancel-${file.lastModified}-${file.name}`, () => {
`cancel-${file.lastModified}-${file.name}`, XHR.abort();
() => { });
XHR.abort();
}
);
XHR.open("post", path, true); XHR.open("post", path, true);
XHR.setRequestHeader("authorization", getCookie(Credentials.session.key));
XHR.onerror = (event) => { XHR.onerror = (event) => {
console.log(event); console.log(event);
XHR.abort(); XHR.abort();
@ -99,10 +105,7 @@ export const upload = async ({ file, context, bucketName }) => {
false false
); );
window.removeEventListener( window.removeEventListener(`cancel-${file.lastModified}-${file.name}`, () => XHR.abort());
`cancel-${file.lastModified}-${file.name}`,
() => XHR.abort()
);
XHR.onloadend = (event) => { XHR.onloadend = (event) => {
console.log("FILE UPLOAD END", event); console.log("FILE UPLOAD END", event);
@ -119,10 +122,15 @@ export const upload = async ({ file, context, bucketName }) => {
let res; let res;
// TODO(jim): Make this smarter. // TODO(jim): Make this smarter.
const storageDealRoute =
routes && routes.storageDealUpload ? routes.storageDealUpload : `/api/data/deal/`;
const generalRoute = routes && routes.upload ? routes.upload : "/api/data/";
if (bucketName && bucketName === STAGING_DEAL_BUCKET) { if (bucketName && bucketName === STAGING_DEAL_BUCKET) {
res = await _privateUploadMethod(`/api/data/deal/${file.name}`, file); res = await _privateUploadMethod(`${storageDealRoute}${file.name}`, file);
} else { } else {
res = await _privateUploadMethod(`/api/data/${file.name}`, file); res = await _privateUploadMethod(`${generalRoute}${file.name}`, file);
} }
if (!res || res.error || !res.data) { if (!res || res.error || !res.data) {
@ -176,8 +184,7 @@ export const uploadToSlate = async ({ responses, slate }) => {
name: "create-alert", name: "create-alert",
detail: { detail: {
alert: { alert: {
message: message: "We're having trouble connecting right now. Please try again later",
"We're having trouble connecting right now. Please try again later",
}, },
}, },
}); });
@ -193,9 +200,7 @@ export const uploadToSlate = async ({ responses, slate }) => {
skipped = addResponse.skipped; skipped = addResponse.skipped;
} }
} }
let message = `${added || 0} file${ let message = `${added || 0} file${added !== 1 ? "s" : ""} uploaded to slate. `;
added !== 1 ? "s" : ""
} uploaded to slate. `;
if (skipped) { if (skipped) {
message += `${skipped || 0} duplicate / existing file${ message += `${skipped || 0} duplicate / existing file${
added !== 1 ? "s were" : " was" added !== 1 ? "s were" : " was"

View File

@ -303,6 +303,7 @@ export default class ApplicationPage extends React.Component {
response = await FileUtilities.upload({ response = await FileUtilities.upload({
file: files[i], file: files[i],
context: this, context: this,
routes: this.props.resources,
}); });
} catch (e) { } catch (e) {
console.log(e); console.log(e);
@ -817,6 +818,7 @@ export default class ApplicationPage extends React.Component {
onRehydrate: this.rehydrate, onRehydrate: this.rehydrate,
sceneId: current.target.id, sceneId: current.target.id,
mobile: this.state.mobile, mobile: this.state.mobile,
resources: this.props.resources,
}); });
let sidebarElement; let sidebarElement;
@ -837,6 +839,7 @@ export default class ApplicationPage extends React.Component {
onSidebarLoading: this._handleSidebarLoading, onSidebarLoading: this._handleSidebarLoading,
onAction: this._handleAction, onAction: this._handleAction,
onRehydrate: this.rehydrate, onRehydrate: this.rehydrate,
resources: this.props.resources,
}); });
} }

View File

@ -14,8 +14,7 @@ export const POSTGRES_ADMIN_USERNAME = process.env.POSTGRES_ADMIN_USERNAME;
export const POSTGRES_HOSTNAME = process.env.POSTGRES_HOSTNAME; export const POSTGRES_HOSTNAME = process.env.POSTGRES_HOSTNAME;
export const POSTGRES_DATABASE = process.env.POSTGRES_DATABASE; export const POSTGRES_DATABASE = process.env.POSTGRES_DATABASE;
export const JWT_SECRET = process.env.JWT_SECRET; export const JWT_SECRET = process.env.JWT_SECRET;
export const LOCAL_PASSWORD_ROUNDS_MANUAL = export const LOCAL_PASSWORD_ROUNDS_MANUAL = process.env.LOCAL_PASSWORD_ROUNDS_MANUAL;
process.env.LOCAL_PASSWORD_ROUNDS_MANUAL;
export const LOCAL_PASSWORD_ROUNDS = process.env.LOCAL_PASSWORD_ROUNDS; export const LOCAL_PASSWORD_ROUNDS = process.env.LOCAL_PASSWORD_ROUNDS;
// TODO(jim): // TODO(jim):
@ -34,3 +33,7 @@ export const TEXTILE_HUB_STAGING_HOST = process.env.TEXTILE_HUB_STAGING_HOST;
export const SOCIAL_SLACK_WEBHOOK_KEY = process.env.SOCIAL_SLACK_WEBHOOK_KEY; export const SOCIAL_SLACK_WEBHOOK_KEY = process.env.SOCIAL_SLACK_WEBHOOK_KEY;
export const SUPPORT_SLACK_WEBHOOK_KEY = process.env.SUPPORT_SLACK_WEBHOOK_KEY; export const SUPPORT_SLACK_WEBHOOK_KEY = process.env.SUPPORT_SLACK_WEBHOOK_KEY;
export const TEXTILE_SLACK_WEBHOOK_KEY = process.env.TEXTILE_SLACK_WEBHOOK_KEY; export const TEXTILE_SLACK_WEBHOOK_KEY = process.env.TEXTILE_SLACK_WEBHOOK_KEY;
// NOTE(jim): customize resources
export const RESOURCE_URI_UPLOAD = process.env.RESOURCE_URI_UPLOAD;
export const RESOURCE_URI_STORAGE_UPLOAD = process.env.RESOURCE_URI_STORAGE_UPLOAD;

View File

@ -19,32 +19,20 @@ export const init = (middleware) => {
}; };
export const CORS = async (req, res, next) => { export const CORS = async (req, res, next) => {
/*
res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Methods", "GET, POST, PATCH, PUT, DELETE, OPTIONS");
res.header( res.header("Access-Control-Allow-Headers", "Origin, Accept, Content-Type, Authorization");
"Access-Control-Allow-Methods",
"GET, POST, PATCH, PUT, DELETE, OPTIONS"
);
res.header(
"Access-Control-Allow-Headers",
"Origin, Accept, Content-Type, Authorization"
);
if (req.method === "OPTIONS") { if (req.method === "OPTIONS") {
return res.status(200).end(); return res.status(200).end();
} }
*/
next(); next();
}; };
export const RequireCookieAuthentication = async (req, res, next) => { export const RequireCookieAuthentication = async (req, res, next) => {
if (Strings.isEmpty(req.headers.cookie)) { if (Strings.isEmpty(req.headers.cookie)) {
return res return res.status(403).json({ decorator: "SERVER_AUTH_USER_NO_TOKEN", error: true });
.status(403)
.json({ decorator: "SERVER_AUTH_USER_NO_TOKEN", error: true });
} }
const token = req.headers.cookie.replace( const token = req.headers.cookie.replace(
@ -60,15 +48,11 @@ export const RequireCookieAuthentication = async (req, res, next) => {
}); });
if (!user || user.error) { if (!user || user.error) {
return res return res.status(403).json({ decorator: "SERVER_AUTH_USER_NOT_FOUND", error: true });
.status(403)
.json({ decorator: "SERVER_AUTH_USER_NOT_FOUND", error: true });
} }
} catch (err) { } catch (err) {
console.log(err); console.log(err);
return res return res.status(403).json({ decorator: "SERVER_AUTH_USER_ERROR", error: true });
.status(403)
.json({ decorator: "SERVER_AUTH_USER_ERROR", error: true });
} }
next(); next();

View File

@ -8,6 +8,7 @@ export const getServerSideProps = async ({ query }) => {
viewer: query.viewer, viewer: query.viewer,
analytics: query.analytics, analytics: query.analytics,
mobile: query.mobile, mobile: query.mobile,
resources: query.resources,
}, },
}; };
}; };
@ -19,6 +20,7 @@ export default class ApplicationPage extends React.Component {
viewer={this.props.viewer} viewer={this.props.viewer}
analytics={this.props.analytics} analytics={this.props.analytics}
mobile={this.props.mobile} mobile={this.props.mobile}
resources={this.props.resources}
/> />
); );
} }

View File

@ -19,9 +19,7 @@ export default async (req, res) => {
}); });
if (!user || user.error) { if (!user || user.error) {
return res return res.status(403).send({ decorator: "UPLOAD_NOT_ALLOWED", error: true });
.status(403)
.send({ decorator: "UPLOAD_NOT_ALLOWED", error: true });
} }
let response = null; let response = null;
@ -35,15 +33,11 @@ export default async (req, res) => {
} }
if (!response) { if (!response) {
return res return res.status(413).send({ decorator: "SERVER_UPLOAD_ERROR", error: true });
.status(413)
.send({ decorator: "SERVER_UPLOAD_ERROR", error: true });
} }
if (response.error) { if (response.error) {
return res return res.status(413).send({ decorator: response.decorator, error: response.error });
.status(413)
.send({ decorator: response.decorator, error: response.error });
} }
const { data, ipfs } = response; const { data, ipfs } = response;
@ -52,14 +46,11 @@ export default async (req, res) => {
ipfs, ipfs,
}); });
const slateId = req.params ? req.params.b : null;
return res.status(200).send({ return res.status(200).send({
decorator: "SERVER_UPLOAD", decorator: "SERVER_UPLOAD",
data: { data: {
data: finalData, data: finalData,
owner_user_id: user.id, owner_user_id: user.id,
slate_id: slateId,
}, },
}); });
}; };

View File

@ -22,9 +22,7 @@ export default async (req, res) => {
}); });
if (!user || user.error) { if (!user || user.error) {
return res return res.status(403).send({ decorator: "UPLOAD_NOT_ALLOWED", error: true });
.status(403)
.send({ decorator: "UPLOAD_NOT_ALLOWED", error: true });
} }
let response = null; let response = null;
@ -39,15 +37,11 @@ export default async (req, res) => {
} }
if (!response) { if (!response) {
return res return res.status(413).send({ decorator: "SERVER_UPLOAD_ERROR", error: true });
.status(413)
.send({ decorator: "SERVER_UPLOAD_ERROR", error: true });
} }
if (response.error) { if (response.error) {
return res return res.status(413).send({ decorator: response.decorator, error: response.error });
.status(413)
.send({ decorator: response.decorator, error: response.error });
} }
return res.status(200).send({ return res.status(200).send({

View File

@ -38,12 +38,9 @@ export default class SceneEditAccount extends React.Component {
photo: this.props.viewer.data.photo, photo: this.props.viewer.data.photo,
name: this.props.viewer.data.name, name: this.props.viewer.data.name,
deleting: false, deleting: false,
allow_filecoin_directory_listing: this.props.viewer allow_filecoin_directory_listing: this.props.viewer.allow_filecoin_directory_listing,
.allow_filecoin_directory_listing, allow_automatic_data_storage: this.props.viewer.allow_automatic_data_storage,
allow_automatic_data_storage: this.props.viewer allow_encrypted_data_storage: this.props.viewer.allow_encrypted_data_storage,
.allow_automatic_data_storage,
allow_encrypted_data_storage: this.props.viewer
.allow_encrypted_data_storage,
changingPassword: false, changingPassword: false,
changingUsername: false, changingUsername: false,
changingAvatar: false, changingAvatar: false,
@ -78,7 +75,7 @@ export default class SceneEditAccount extends React.Component {
return; return;
} }
const response = await FileUtilities.upload({ file }); const response = await FileUtilities.upload({ file, routes: this.props.resources });
if (!response) { if (!response) {
dispatchCustomEvent({ dispatchCustomEvent({
@ -140,8 +137,7 @@ export default class SceneEditAccount extends React.Component {
photo: this.state.photo, photo: this.state.photo,
body: this.state.body, body: this.state.body,
name: this.state.name, name: this.state.name,
allow_filecoin_directory_listing: this.state allow_filecoin_directory_listing: this.state.allow_filecoin_directory_listing,
.allow_filecoin_directory_listing,
allow_automatic_data_storage: this.state.allow_automatic_data_storage, allow_automatic_data_storage: this.state.allow_automatic_data_storage,
allow_encrypted_data_storage: this.state.allow_encrypted_data_storage, allow_encrypted_data_storage: this.state.allow_encrypted_data_storage,
}, },
@ -247,19 +243,10 @@ export default class SceneEditAccount extends React.Component {
description="This image will appear in various lists." description="This image will appear in various lists."
/> />
<Avatar <Avatar style={{ marginTop: 24 }} size={256} url={this.props.viewer.data.photo} />
style={{ marginTop: 24 }}
size={256}
url={this.props.viewer.data.photo}
/>
<div style={{ marginTop: 24 }}> <div style={{ marginTop: 24 }}>
<input <input css={STYLES_FILE_HIDDEN} type="file" id="file" onChange={this._handleUpload} />
css={STYLES_FILE_HIDDEN}
type="file"
id="file"
onChange={this._handleUpload}
/>
<System.ButtonPrimary <System.ButtonPrimary
style={{ margin: "0 16px 16px 0" }} style={{ margin: "0 16px 16px 0" }}
type="label" type="label"
@ -282,8 +269,7 @@ export default class SceneEditAccount extends React.Component {
value={this.state.allow_filecoin_directory_listing} value={this.state.allow_filecoin_directory_listing}
onChange={this._handleCheckboxChange} onChange={this._handleCheckboxChange}
> >
Show your successful deals on a directory page where others can Show your successful deals on a directory page where others can retrieve them.
retrieve them.
</System.CheckBox> </System.CheckBox>
<System.CheckBox <System.CheckBox
@ -292,8 +278,8 @@ export default class SceneEditAccount extends React.Component {
value={this.state.allow_automatic_data_storage} value={this.state.allow_automatic_data_storage}
onChange={this._handleCheckboxChange} onChange={this._handleCheckboxChange}
> >
Allow Slate to make archive storage deals on your behalf to the Allow Slate to make archive storage deals on your behalf to the Filecoin Network. You will
Filecoin Network. You will get a receipt in the Filecoin section. get a receipt in the Filecoin section.
</System.CheckBox> </System.CheckBox>
<System.CheckBox <System.CheckBox
@ -302,8 +288,8 @@ export default class SceneEditAccount extends React.Component {
value={this.state.allow_encrypted_data_storage} value={this.state.allow_encrypted_data_storage}
onChange={this._handleCheckboxChange} onChange={this._handleCheckboxChange}
> >
Force encryption on archive storage deals (only you can see retrieved Force encryption on archive storage deals (only you can see retrieved data from the
data from the Filecoin network). Filecoin network).
</System.CheckBox> </System.CheckBox>
<div style={{ marginTop: 24 }}> <div style={{ marginTop: 24 }}>
@ -320,8 +306,7 @@ export default class SceneEditAccount extends React.Component {
label="Username" label="Username"
description={ description={
<React.Fragment> <React.Fragment>
This is your username on Slate. Your username is unique and used This is your username on Slate. Your username is unique and used for your profile URL{" "}
for your profile URL{" "}
<a href={profileURL} target="_blank"> <a href={profileURL} target="_blank">
{profileURL} {profileURL}
</a> </a>
@ -334,10 +319,7 @@ export default class SceneEditAccount extends React.Component {
/> />
<div style={{ marginTop: 24 }}> <div style={{ marginTop: 24 }}>
<System.ButtonPrimary <System.ButtonPrimary onClick={this._handleSave} loading={this.state.changingUsername}>
onClick={this._handleSave}
loading={this.state.changingUsername}
>
Change username Change username
</System.ButtonPrimary> </System.ButtonPrimary>
</div> </div>
@ -363,10 +345,7 @@ export default class SceneEditAccount extends React.Component {
/> />
<div style={{ marginTop: 24 }}> <div style={{ marginTop: 24 }}>
<System.ButtonPrimary <System.ButtonPrimary onClick={this._handleSaveBio} loading={this.state.changingBio}>
onClick={this._handleSaveBio}
loading={this.state.changingBio}
>
Update information Update information
</System.ButtonPrimary> </System.ButtonPrimary>
</div> </div>
@ -413,10 +392,7 @@ export default class SceneEditAccount extends React.Component {
/> />
<div style={{ marginTop: 24 }}> <div style={{ marginTop: 24 }}>
<System.ButtonPrimary <System.ButtonPrimary onClick={this._handleDelete} loading={this.state.deleting}>
onClick={this._handleDelete}
loading={this.state.deleting}
>
Delete my account Delete my account
</System.ButtonPrimary> </System.ButtonPrimary>
</div> </div>

View File

@ -102,6 +102,7 @@ export default class SceneMakeFilecoinDeal extends React.Component {
const response = await FileUtilities.upload({ const response = await FileUtilities.upload({
bucketName: STAGING_DEAL_BUCKET, bucketName: STAGING_DEAL_BUCKET,
routes: this.props.resources,
file, file,
}); });
} }

View File

@ -93,6 +93,10 @@ app.prepare().then(async () => {
viewer, viewer,
analytics, analytics,
mobile, mobile,
resources: {
storageDealUpload: Environment.RESOURCE_URI_STORAGE_UPLOAD,
upload: Environment.RESOURCE_URI_UPLOAD,
},
}); });
}); });
@ -119,7 +123,13 @@ app.prepare().then(async () => {
// TODO(jim): Temporary workaround // TODO(jim): Temporary workaround
if (!Validations.userRoute(req.params.username)) { if (!Validations.userRoute(req.params.username)) {
return handler(req, res, req.url, { mobile }); return handler(req, res, req.url, {
mobile,
resources: {
storageDealUpload: Environment.RESOURCE_URI_STORAGE_UPLOAD,
upload: Environment.RESOURCE_URI_UPLOAD,
},
});
} }
const id = Utilities.getIdFromCookie(req); const id = Utilities.getIdFromCookie(req);
@ -152,6 +162,10 @@ app.prepare().then(async () => {
viewer, viewer,
creator: Serializers.user({ ...creator, slates }), creator: Serializers.user({ ...creator, slates }),
mobile, mobile,
resources: {
storageDealUpload: Environment.RESOURCE_URI_STORAGE_UPLOAD,
upload: Environment.RESOURCE_URI_UPLOAD,
},
}); });
}); });
@ -160,7 +174,13 @@ app.prepare().then(async () => {
// TODO(jim): Temporary workaround // TODO(jim): Temporary workaround
if (!Validations.userRoute(req.params.username)) { if (!Validations.userRoute(req.params.username)) {
return handler(req, res, req.url, { mobile }); return handler(req, res, req.url, {
mobile,
resources: {
storageDealUpload: Environment.RESOURCE_URI_STORAGE_UPLOAD,
upload: Environment.RESOURCE_URI_UPLOAD,
},
});
} }
const slate = await Data.getSlateByName({ const slate = await Data.getSlateByName({
@ -208,6 +228,10 @@ app.prepare().then(async () => {
creator: Serializers.user(creator), creator: Serializers.user(creator),
slate, slate,
mobile, mobile,
resources: {
storageDealUpload: Environment.RESOURCE_URI_STORAGE_UPLOAD,
upload: Environment.RESOURCE_URI_UPLOAD,
},
}); });
}); });