removed storage deal options and made edits to storage deal script

This commit is contained in:
Martina 2021-09-28 14:54:02 -07:00
parent b2ddaf183c
commit c3f2d7c617
15 changed files with 17 additions and 1722 deletions

View File

@ -134,14 +134,6 @@ export const sendTemplateEmail = async (data) => {
});
};
export const archive = async (data) => {
await Websockets.checkWebsocket();
return await returnJSON(`/api/data/archive`, {
...DEFAULT_OPTIONS,
body: JSON.stringify({ data }),
});
};
export const removeFromBucket = async (data) => {
await Websockets.checkWebsocket();
return await returnJSON(`/api/data/bucket-remove`, {

View File

@ -172,18 +172,6 @@ export const navigation = [
pathname: "/_/directory",
},
slatePage,
{
id: "NAV_FILECOIN",
name: "Filecoin",
pageTitle: "Archive on Filecoin",
pathname: "/_/filecoin",
},
{
id: "NAV_STORAGE_DEAL",
name: "Storage Deal",
pageTitle: "Filecoin Storage Deal",
pathname: "/_/storage-deal",
},
{
id: "NAV_API",
name: "API",

View File

@ -27,15 +27,12 @@ import SceneSlate from "~/scenes/SceneSlate";
import SceneActivity from "~/scenes/SceneActivity";
import SceneDirectory from "~/scenes/SceneDirectory";
import SceneProfile from "~/scenes/SceneProfile";
import SceneArchive from "~/scenes/SceneArchive";
import SceneMakeFilecoinDeal from "~/scenes/SceneMakeFilecoinDeal";
// NOTE(jim):
// Sidebars each have a decorator and can be shown to with _handleAction
import SidebarCreateSlate from "~/components/sidebars/SidebarCreateSlate";
import SidebarCreateWalletAddress from "~/components/sidebars/SidebarCreateWalletAddress";
import SidebarWalletSendFunds from "~/components/sidebars/SidebarWalletSendFunds";
import SidebarFileStorageDeal from "~/components/sidebars/SidebarFileStorageDeal";
import SidebarAddFileToSlate from "~/components/sidebars/SidebarAddFileToSlate";
import SidebarDragDropNotice from "~/components/sidebars/SidebarDragDropNotice";
import SidebarSingleSlateSettings from "~/components/sidebars/SidebarSingleSlateSettings";
@ -62,7 +59,6 @@ import { LoaderSpinner } from "~/components/system/components/Loaders";
const SIDEBARS = {
SIDEBAR_FILECOIN_ARCHIVE: <SidebarFilecoinArchive />,
SIDEBAR_FILE_STORAGE_DEAL: <SidebarFileStorageDeal />,
SIDEBAR_WALLET_SEND_FUNDS: <SidebarWalletSendFunds />,
SIDEBAR_CREATE_WALLET_ADDRESS: <SidebarCreateWalletAddress />,
SIDEBAR_ADD_FILE_TO_SLATE: <SidebarAddFileToSlate />,
@ -86,8 +82,6 @@ const SCENES = {
NAV_API: <SceneSettingsDeveloper />,
NAV_SETTINGS: <SceneEditAccount />,
NAV_SLATES: <SceneSlates />,
NAV_FILECOIN: <SceneArchive />,
NAV_STORAGE_DEAL: <SceneMakeFilecoinDeal />,
};
let mounted;

View File

@ -196,24 +196,6 @@ export class ApplicationUserControlsPopup extends React.Component {
},
],
[
{
text: (
<div css={STYLES_SECTION_ITEM_HOVER}>
<Link href={"/_/filecoin"} onAction={this._handleAction}>
Filecoin
</Link>
</div>
),
},
{
text: (
<div css={STYLES_SECTION_ITEM_HOVER}>
<Link href={"/_/storage-deal"} onAction={this._handleAction}>
Storage deal
</Link>
</div>
),
},
{
text: (
<div css={STYLES_SECTION_ITEM_HOVER}>

View File

@ -1,164 +0,0 @@
import * as React from "react";
import * as Strings from "~/common/strings";
import * as Constants from "~/common/constants";
import * as System from "~/components/system";
import { css } from "@emotion/react";
const STYLES_FOCUS = css`
font-size: ${Constants.typescale.lvl1};
font-family: ${Constants.font.medium};
overflow-wrap: break-word;
width: 100%;
strong {
font-family: ${Constants.font.semiBold};
font-weight: 400;
}
`;
const STYLES_SUBTEXT = css`
margin-top: 8px;
font-size: 12px;
`;
const STYLES_ITEM = css`
margin-top: 16px;
`;
const STYLES_IMAGE_PREVIEW = css`
display: block;
width: 100%;
margin-top: 48px;
`;
export default class SidebarFileStorageDeal extends React.Component {
state = {
settings_cold_default_duration: this.props.viewer.settings_cold_default_duration,
settings_cold_default_replication_factor: this.props.viewer
.settings_cold_default_replication_factor,
loading: false,
};
async componentDidMount() {
if (!this.props.viewer.settingsDealsAutoApprove) {
return null;
}
await this._handleSubmit();
}
_handleMakeDeal = async ({ ipfs }) => {
const options = {
method: "POST",
credentials: "include",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({ ipfs }),
};
const response = await fetch("/api/data/storage-deal", options);
const json = await response.json();
return json;
};
_handleSubmit = async (e) => {
if (e) {
e.persist();
}
this.setState({ loading: true });
await this._handleMakeDeal({ ipfs: `/ipfs/${this.props.data.cid}` });
this.setState({ loading: false });
await this.props.onCancel();
};
_handleCancel = () => {
this.props.onCancel();
};
_handleChange = (e) => {
this.setState({ [e.target.name]: e.target.value });
};
render() {
const file = this.props.data;
return (
<React.Fragment>
<System.P1
style={{
fontFamily: Constants.font.semiBold,
fontSize: Constants.typescale.lvl3,
}}
>
Make Filecoin storage deal
</System.P1>
<div>
<div css={STYLES_ITEM}>
<div css={STYLES_FOCUS}>{file.name}</div>
<div css={STYLES_SUBTEXT}>Name</div>
</div>
<div css={STYLES_ITEM}>
<div css={STYLES_FOCUS}>{Strings.bytesToSize(file.size)}</div>
<div css={STYLES_SUBTEXT}>File size</div>
</div>
</div>
{!this.state.loading ? (
<System.Input
containerStyle={{ marginTop: 48 }}
label="Deal duration"
name="settings_cold_default_duration"
placeholder="Type in epochs (~25 seconds)"
type="number"
value={this.state.settings_cold_default_duration}
onChange={this._handleChange}
/>
) : null}
{!this.state.loading ? (
<System.Input
containerStyle={{ marginTop: 24 }}
label="Replication factor"
name="settings_cold_default_replication_factor"
value={this.state.settings_cold_default_replication_factor}
onChange={this._handleChange}
/>
) : null}
{!this.state.loading ? (
<System.SelectMenu
full
containerStyle={{ marginTop: 24 }}
name="address"
label="Payment address"
value={this.props.selected.address}
category="address"
onChange={this.props.onSelectedChange}
options={this.props.viewer.addresses}
/>
) : null}
<System.ButtonPrimary
full
style={{ marginTop: 48 }}
onClick={this._handleSubmit}
loading={this.state.loading}
>
Make storage deal
</System.ButtonPrimary>
{!this.state.loading ? (
<System.ButtonSecondary full style={{ marginTop: 16 }} onClick={this._handleCancel}>
Cancel deal
</System.ButtonSecondary>
) : null}
</React.Fragment>
);
}
}

View File

@ -22,9 +22,6 @@
// textileToken: user.textileToken,
// textileThreadID: user.textileThreadID,
// textileBucketCID: user.textileThreadID,
// settingsDealAutoApprove: user.settingsDealAutoApprove,
// allowAutomaticDataStorage: user.allowAutomaticDataStorage,
// allowEncryptedDataStorage: user.allowEncryptedDataStorage,
// onboarding: user.onboarding,
// };
// };

View File

@ -1,206 +0,0 @@
import * as Constants from "~/node_common/constants";
import * as Utilities from "~/node_common/utilities";
import * as Social from "~/node_common/social";
import * as Strings from "~/common/strings";
import * as Logging from "~/common/logging";
import * as RequestUtilities from "~/node_common/request-utilities";
import { v4 as uuid } from "uuid";
import { MAX_BUCKET_COUNT, MIN_ARCHIVE_SIZE_BYTES } from "~/node_common/constants";
export default async (req, res) => {
const userInfo = await RequestUtilities.checkAuthorizationInternal(req, res);
if (!userInfo) return;
const { id, user } = userInfo;
let bucketName = Constants.textile.dealsBucket;
if (req.body.data && req.body.data.bucketName) {
bucketName = req.body.data.bucketName;
}
const { buckets, bucketKey } = await Utilities.getBucket({
user,
bucketName,
});
if (!buckets) {
return res.status(500).send({
decorator: "SERVER_NO_BUCKET_DATA",
error: true,
});
}
// NOTE(jim): Getting the appropriate bucket key
let items = null;
let bucketSizeBytes = 0;
try {
const path = await buckets.listPath(bucketKey, "/");
items = path.item;
bucketSizeBytes = path.item.size;
} catch (e) {
Social.sendTextileSlackMessage({
file: "/pages/api/data/archive.js",
user,
message: e.message,
code: e.code,
functionName: `buckets.listPath`,
});
}
if (!items) {
return res.status(500).send({
decorator: "SERVER_NO_BUCKET_DATA",
error: true,
});
}
Logging.log(`[ deal ] will make a deal for ${items.items.length} items`);
if (items.items.length < 2) {
return res.status(500).send({
decorator: "SERVER_ARCHIVE_NO_FILES",
error: true,
});
}
Logging.log(`[ deal ] deal size: ${Strings.bytesToSize(bucketSizeBytes)}`);
if (bucketSizeBytes < MIN_ARCHIVE_SIZE_BYTES) {
return res.status(500).send({
decorator: "SERVER_ARCHIVE_BUCKET_TOO_SMALL",
message: `Your deal size of ${Strings.bytesToSize(
bucketSizeBytes
)} is too small. You must provide at least 100MB.`,
error: true,
});
}
// NOTE(jim): Make sure that you haven't hit the MAX_BUCKET_COUNT
let userBuckets = [];
try {
userBuckets = await buckets.list();
} catch (e) {
Social.sendTextileSlackMessage({
file: "/pages/api/data/archive.js",
user: user,
message: e.message,
code: e.code,
functionName: `buckets.list`,
});
return res.status(500).send({
decorator: "SERVER_ARCHIVE_BUCKET_COUNT_VERIFICATION_FAILED",
error: true,
});
}
Logging.log(
`[ encrypted ] user has ${userBuckets.length} out of ${MAX_BUCKET_COUNT} buckets used.`
);
if (userBuckets.length >= MAX_BUCKET_COUNT) {
return res.status(500).send({
decorator: "SERVER_ARCHIVE_MAX_NUMBER_BUCKETS",
error: true,
});
}
// NOTE(jim): Either encrypt the bucket or don't encrypt the bucket.
let encryptThisDeal = false;
if (bucketName !== Constants.textile.dealsBucket && user.allowEncryptedDataStorage) {
encryptThisDeal = true;
}
if (req.body.data.forceEncryption) {
encryptThisDeal = true;
}
let key = bucketKey;
let encryptedBucketName = null;
if (user.allowEncryptedDataStorage || req.body.data.forceEncryption) {
encryptedBucketName = req.body.data.forceEncryption
? `encrypted-deal-${uuid()}`
: `encrypted-data-${uuid()}`;
Logging.log(`[ encrypted ] making an ${encryptedBucketName} for this storage deal.`);
try {
const newBucket = await buckets.create(encryptedBucketName, true, items.cid);
key = newBucket.root.key;
} catch (e) {
Social.sendTextileSlackMessage({
file: "/pages/api/data/archive.js",
user: user,
message: e.message,
code: e.code,
functionName: `buckets.create (encrypted)`,
});
return res.status(500).send({
decorator: "SERVER_ARCHIVE_ENCRYPTION_FAILED",
error: true,
});
}
Logging.log(`[ encrypted ] ${encryptedBucketName}`);
Logging.log(`[ encrypted ] ${key}`);
} else {
const newDealBucketName = `open-deal-${uuid()}`;
try {
const newBucket = await buckets.create(newDealBucketName, false, items.cid);
key = newBucket.root.key;
} catch (e) {
Social.sendTextileSlackMessage({
file: "/pages/api/data/archive.js",
user: user,
message: e.message,
code: e.code,
functionName: `buckets.create (normal, not encrypted)`,
});
return res.status(500).send({
decorator: "SERVER_ARCHIVE_BUCKET_CLONING_FAILED",
error: true,
});
}
Logging.log(`[ normal ] ${newDealBucketName}`);
Logging.log(`[ normal ] ${key}`);
}
// NOTE(jim): Finally make the deal
let response = {};
let error = {};
try {
Logging.log(`[ deal-maker ] deal being made for ${key}`);
if (req.body.data && req.body.data.settings) {
response = await buckets.archive(key, req.body.data.settings);
} else {
response = await buckets.archive(key);
}
} catch (e) {
error.message = e.message;
error.code = e.code;
Logging.log(e.message);
Social.sendTextileSlackMessage({
file: "/pages/api/data/archive.js",
user: user,
message: e.message,
code: e.code,
functionName: `buckets.archive`,
});
return res.status(500).send({
decorator: "SERVER_ARCHIVE_DEAL_FAILED",
error: true,
message: e.message,
});
}
return res.status(200).send({
decorator: "SERVER_ARCHIVE",
data: { response, error },
});
};

View File

@ -1,200 +0,0 @@
import * as React from "react";
import * as System from "~/components/system";
import * as Constants from "~/common/constants";
import * as Actions from "~/common/actions";
import * as Strings from "~/common/strings";
import { css } from "@emotion/react";
import { LoaderSpinner } from "~/components/system/components/Loaders";
import { SecondaryTabGroup } from "~/components/core/TabGroup";
import WebsitePrototypeWrapper from "~/components/core/WebsitePrototypeWrapper";
import ScenePage from "~/components/core/ScenePage";
import ScenePageHeader from "~/components/core/ScenePageHeader";
import SceneSettings from "~/scenes/SceneSettings";
import SceneDeals from "~/scenes/SceneDeals";
import SceneWallet from "~/scenes/SceneWallet";
const STYLES_SPINNER_CONTAINER = css`
width: 100%;
height: 40vh;
display: flex;
align-items: center;
justify-content: center;
`;
let mounted = false;
export default class SceneArchive extends React.Component {
state = {
deals: [],
dealsLoaded: false,
networkViewer: null,
allowAutomaticDataStorage: this.props.viewer.allowAutomaticDataStorage,
allowEncryptedDataStorage: this.props.viewer.allowEncryptedDataStorage,
};
async componentDidMount() {
if (mounted) {
return null;
}
mounted = true;
let networkViewer;
try {
const response = await fetch("/api/network");
const json = await response.json();
networkViewer = json.data;
} catch (e) {}
this.setState({
networkViewer,
});
let deals = [];
try {
const response = await fetch("/api/network-deals");
const json = await response.json();
deals = json.data.deals;
} catch (e) {}
if (!deals || !deals.length) {
this.setState({ dealsLoaded: true });
}
this.setState({ deals, dealsLoaded: true });
}
_handleCheckboxChange = (e) => {
this.setState({ [e.target.name]: e.target.value });
};
_handleSaveFilecoin = async (e) => {
this.setState({ changingFilecoin: true });
await Actions.updateViewer({
user: {
allowAutomaticDataStorage: this.state.allowAutomaticDataStorage,
allowEncryptedDataStorage: this.state.allowEncryptedDataStorage,
},
});
this.setState({ changingFilecoin: false });
};
componentWillUnmount() {
mounted = false;
}
render() {
let tab = this.props.page.params?.tab || "archive";
return (
<WebsitePrototypeWrapper
title={`${this.props.page.pageTitle} • Slate`}
url={`${Constants.hostname}${this.props.page.pathname}`}
>
<ScenePage>
<ScenePageHeader title="Filecoin">
{/* Use this section to archive all of your data on to Filecoin through a storage deal. You
must have at last 100MB stored to make an archive storage deal. */}
</ScenePageHeader>
<SecondaryTabGroup
tabs={[
{ title: "Archive Settings", value: { tab: "archive" } },
{ title: "Wallet", value: { tab: "wallet" } },
]}
value={tab}
onAction={this.props.onAction}
/>
{this.state.networkViewer ? (
<React.Fragment>
{tab === "archive" ? (
<React.Fragment>
<ScenePageHeader>
Use this section to archive all of your data on to Filecoin through a storage
deal. You must have at last 100MB stored to make an archive storage deal.
</ScenePageHeader>
<System.P1 style={{ marginTop: 24 }}>
Archive all of your data onto the Filecoin Network with a storage deal using
your default settings.
</System.P1>
<br />
<System.ButtonPrimary
onClick={() =>
this.props.onAction({
type: "SIDEBAR",
value: "SIDEBAR_FILECOIN_ARCHIVE",
})
}
>
Archive your data
</System.ButtonPrimary>
<System.DescriptionGroup
style={{ marginTop: 64 }}
label="Archive automation settings"
description="Configure the automation settings for your archive storage deals."
/>
<System.CheckBox
style={{ marginTop: 24 }}
name="allowAutomaticDataStorage"
value={this.state.allowAutomaticDataStorage}
onChange={this._handleCheckboxChange}
>
Allow Slate to make archive storage deals on your behalf to the Filecoin
Network. You will get a receipt in the Filecoin section.
</System.CheckBox>
<System.CheckBox
style={{ marginTop: 24 }}
name="allowEncryptedDataStorage"
value={this.state.allowEncryptedDataStorage}
onChange={this._handleCheckboxChange}
>
Force encryption on archive storage deals (only you can see retrieved data from
the Filecoin network).
</System.CheckBox>
<div style={{ marginTop: 24 }}>
<System.ButtonPrimary
onClick={this._handleSaveFilecoin}
loading={this.state.changingFilecoin}
>
Save archiving settings
</System.ButtonPrimary>
</div>
<br />
<br />
<SceneSettings {...this.props} networkViewer={this.state.networkViewer} />
</React.Fragment>
) : null}
{tab === "wallet" ? (
<React.Fragment>
<SceneWallet {...this.props} networkViewer={this.state.networkViewer} />
<br />
<br />
{this.state.dealsLoaded ? (
<SceneDeals deals={this.state.deals} dealsLoaded={this.state.dealsLoaded} />
) : (
<div css={STYLES_SPINNER_CONTAINER}>
<LoaderSpinner style={{ height: 32, width: 32 }} />
</div>
)}
</React.Fragment>
) : null}
</React.Fragment>
) : (
<div css={STYLES_SPINNER_CONTAINER}>
<LoaderSpinner style={{ height: 32, width: 32 }} />
</div>
)}
</ScenePage>
</WebsitePrototypeWrapper>
);
}
}

View File

@ -50,8 +50,6 @@ export default class SceneEditAccount extends React.Component {
photo: this.props.viewer.photo,
name: this.props.viewer.name,
deleting: false,
allowAutomaticDataStorage: this.props.viewer.allowAutomaticDataStorage,
allowEncryptedDataStorage: this.props.viewer.allowEncryptedDataStorage,
changingPassword: false,
changingAvatar: false,
savingNameBio: false,
@ -80,21 +78,6 @@ export default class SceneEditAccount extends React.Component {
this.setState({ changingAvatar: false, photo: url });
};
_handleSaveFilecoin = async (e) => {
this.setState({ changingFilecoin: true });
let response = await Actions.updateViewer({
user: {
allowAutomaticDataStorage: this.state.allowAutomaticDataStorage,
allowEncryptedDataStorage: this.state.allowEncryptedDataStorage,
},
});
Events.hasError(response);
this.setState({ changingFilecoin: false });
};
_handleSave = async (e) => {
if (!Validations.username(this.state.username)) {
Events.dispatchMessage({
@ -180,7 +163,6 @@ export default class SceneEditAccount extends React.Component {
<SecondaryTabGroup
tabs={[
{ title: "Profile", value: { tab: "profile" } },
{ title: "Data Storage", value: { tab: "storage" } },
{ title: "Security", value: { tab: "security" } },
{ title: "Account", value: { tab: "account" } },
]}
@ -239,48 +221,6 @@ export default class SceneEditAccount extends React.Component {
</div>
</div>
) : null}
{tab === "storage" ? (
<div style={{ maxWidth: 800 }}>
<div css={STYLES_HEADER}>
Allow Slate to make Filecoin archive storage deals on your behalf
</div>
<div style={{ maxWidth: 800 }}>
If this box is checked, then we will make Filecoin archive storage deals on your
behalf. By default these storage deals are not encrypted and anyone can retrieve
them from the Filecoin Network.
</div>
<System.CheckBox
style={{ marginTop: 24 }}
name="allowAutomaticDataStorage"
value={this.state.allowAutomaticDataStorage}
onChange={this._handleChange}
>
Allow Slate to make archive storage deals on your behalf to the Filecoin Network.
You will get a receipt in the Filecoin section.
</System.CheckBox>
<System.CheckBox
style={{ marginTop: 24 }}
name="allowEncryptedDataStorage"
value={this.state.allowEncryptedDataStorage}
onChange={this._handleChange}
>
Force encryption on archive storage deals (only you can see retrieved data from the
Filecoin network).
</System.CheckBox>
<div style={{ marginTop: 24 }}>
<System.ButtonPrimary
onClick={this._handleSaveFilecoin}
loading={this.state.changingFilecoin}
style={{ width: "200px" }}
>
Save
</System.ButtonPrimary>
</div>
</div>
) : null}
{tab === "security" ? (
<div>
<div css={STYLES_HEADER}>Change password</div>

View File

@ -1,531 +0,0 @@
import * as React from "react";
import * as Strings from "~/common/strings";
import * as Actions from "~/common/actions";
import * as Constants from "~/common/constants";
import * as System from "~/components/system";
import * as SVG from "~/common/svg";
import * as Window from "~/common/window";
import * as Messages from "~/common/messages";
import * as FileUtilities from "~/common/file-utilities";
import * as Events from "~/common/custom-events";
import { css } from "@emotion/react";
import { createState } from "~/scenes/SceneSettings";
import { LoaderSpinner } from "~/components/system/components/Loaders";
import { FilecoinNumber } from "@glif/filecoin-number";
import { Link } from "~/components/core/Link";
import WebsitePrototypeWrapper from "~/components/core/WebsitePrototypeWrapper";
import Section from "~/components/core/Section";
import ScenePage from "~/components/core/ScenePage";
import ScenePageHeader from "~/components/core/ScenePageHeader";
const STYLES_SPINNER_CONTAINER = css`
width: 100%;
height: 40vh;
display: flex;
align-items: center;
justify-content: center;
`;
const STYLES_FILE_HIDDEN = css`
height: 1px;
width: 1px;
opacity: 0;
visibility: hidden;
position: fixed;
top: -1px;
left: -1px;
`;
const STYLES_ROW = css`
display: flex;
align-items: flex-start;
justify-content: space-between;
`;
const STYLES_LEFT = css`
color: ${Constants.system.black};
transition: 200ms ease all;
min-width: 10%;
width: 100%;
:visited {
color: ${Constants.system.black};
}
`;
const STYLES_RIGHT = css`
flex-shrink: 0;
transition: 200ms ease all;
cursor: pointer;
:hover {
color: ${Constants.system.blue};
}
`;
const DEFAULT_ERROR_MESSAGE = "We could not make your deal. Please try again later.";
let mounted = false;
export default class SceneMakeFilecoinDeal extends React.Component {
state = { encryption: false };
async componentDidMount() {
if (mounted) {
return;
}
mounted = true;
let networkViewer;
try {
const response = await fetch("/api/network");
const json = await response.json();
networkViewer = json.data;
} catch (e) {}
if (networkViewer) {
this.setState({
networkViewer,
...createState(networkViewer.settings),
encryption: false,
});
}
}
_handleUpload = async (e) => {
e.persist();
if (!e.target.files) {
return null;
}
if (!e.target.files.length) {
return null;
}
this.setState({ loading: true });
for (let i = 0; i < e.target.files.length; i++) {
const file = e.target.files[i];
const response = await FileUtilities.upload({
bucketName: Constants.textile.dealsBucket,
file,
});
}
let networkViewer;
try {
const response = await fetch("/api/network");
const json = await response.json();
networkViewer = json.data;
} catch (e) {}
this.setState({
networkViewer,
loading: false,
});
};
_handleArchive = async (e) => {
this.setState({ archiving: true });
const response = await Actions.archive({
bucketName: Constants.textile.dealsBucket,
forceEncryption: this.state.encryption,
settings: {
/**
* RepFactor indicates the desired amount of active deals
* with different miners to store the data. While making deals
* the other attributes of FilConfig are considered for miner selection.
*/
repFactor: Number(this.state.repFactor),
/**
* DealMinDuration indicates the duration to be used when making new deals.
*/
dealMinDuration: this.state.dealMinDuration,
/**
* ExcludedMiners is a set of miner addresses won't be ever be selected
*when making new deals, even if they comply to other filters.
*/
excludedMiners: this.state.excludedMiners,
/**
* TrustedMiners is a set of miner addresses which will be forcibly used
* when making new deals. An empty/nil list disables this feature.
*/
trustedMiners: this.state.trustedMiners,
/**
* Renew indicates deal-renewal configuration.
*/
renew: {
enabled: this.state.renewEnabled,
threshold: this.state.renewThreshold,
},
/**
* CountryCodes indicates that new deals should select miners on specific countries.
*/
countryCodes: [],
/**
* Addr is the wallet address used to store the data in filecoin
*/
addr: this.state.addr,
/**
* MaxPrice is the maximum price that will be spent to store the data, 0 is no max
*/
maxPrice: this.state.maxPrice,
/**
*
* FastRetrieval indicates that created deals should enable the
* fast retrieval feature.
*/
// fastRetrieval: boolean
/**
* DealStartOffset indicates how many epochs in the future impose a
* deadline to new deals being active on-chain. This value might influence
* if miners accept deals, since they should seal fast enough to satisfy
* this constraint.
*/
// dealStartOffset: number
},
});
if (Events.hasError(response)) {
this.setState({ archiving: false });
}
await Window.delay(5000);
alert(
"Your storage deal was put in the queue. This can take up to 36 hours, check back later."
);
this.props.onAction({ type: "NAVIGATE", href: "/_/filecoin" });
};
_handleRemove = async (cid) => {
this.setState({ loading: true });
await Actions.removeFromBucket({ bucketName: Constants.textile.dealsBucket, cid });
let networkViewer;
try {
const response = await fetch("/api/network");
const json = await response.json();
networkViewer = json.data;
} catch (e) {}
this.setState({
networkViewer,
loading: false,
});
};
_handleAddTrustedMiner = () => {
const miner = prompt("Enter the Miner ID to trust.");
if (Strings.isEmpty(miner)) {
return Events.dispatchMessage({ message: "You must provide a miner ID." });
}
if (this.state.trustedMiners.includes(miner)) {
return Events.dispatchMessage({
message: `${miner} is already on your list of miners to try.`,
});
}
this.setState({
trustedMiners: [miner, ...this.state.trustedMiners],
});
};
_handleAddExcludedMiner = () => {
const miner = prompt("Enter the Miner ID to exclude.");
if (Strings.isEmpty(miner)) {
return Events.dispatchMessage({ message: "You must provide a miner ID." });
}
if (this.state.excludedMiners.includes(miner)) {
return Events.dispatchMessage({
message: `${miner} is already on your list of miners to exclude.`,
});
}
this.setState({
excludedMiners: [miner, ...this.state.excludedMiners],
});
};
_handleRemoveTrustedMiner = (minerId) => {
this.setState({
trustedMiners: this.state.trustedMiners.filter((m) => m !== minerId),
});
};
_handleRemoveExcludedMiner = (minerId) => {
this.setState({
excludedMiners: this.state.excludedMiners.filter((m) => m !== minerId),
});
};
_handleChange = (e) => {
this.setState({ [e.target.name]: e.target.value });
};
componentWillUnmount() {
mounted = false;
}
render() {
const { networkViewer } = this.state;
const addressMap = {};
const addresses = [];
let selected = null;
let balance = 0;
if (networkViewer) {
// TODO(jim): restore this when the wallet function is back in Pow.
}
let inFil = 0;
if (networkViewer) {
const filecoinNumber = new FilecoinNumber(`${this.state.maxPrice}`, "attofil");
inFil = filecoinNumber.toFil();
}
return (
<WebsitePrototypeWrapper
title={`${this.props.page.pageTitle} • Slate`}
url={`${Constants.hostname}${this.props.page.pathname}`}
>
<ScenePage>
<input
css={STYLES_FILE_HIDDEN}
multiple
type="file"
id="file"
onChange={this._handleUpload}
/>
<ScenePageHeader title="Make a one-off Filecoin Storage Deal">
Upload data and make one-off storage deals in the Filecoin network here. Files must be
at least 100MB in size.
</ScenePageHeader>
{this.state.networkViewer ? (
<React.Fragment>
<System.DescriptionGroup
style={{ marginTop: 48, maxWidth: 688 }}
label="Storage deal files"
description="You can add up to 4GB of files."
/>
<Section
style={{ marginTop: 24, maxWidth: 688, minWidth: "auto" }}
onAction={this.props.onAction}
buttons={[
{
name: "Add file",
multiple: true,
type: "file",
id: "file",
},
]}
>
{this.state.loading ? (
<div css={STYLES_SPINNER_CONTAINER}>
<LoaderSpinner style={{ height: 32, width: 32 }} />
</div>
) : (
<System.Table
data={{
columns: [
{
key: "cid",
name: "CID",
width: "100%",
},
],
rows: this.state.networkViewer.deal.map((file) => {
return {
cid: (
<div css={STYLES_ROW}>
<span css={STYLES_LEFT} target="_blank">
{file.cid}
</span>
<span css={STYLES_RIGHT} onClick={() => this._handleRemove(file.cid)}>
<SVG.Dismiss height="16px" />
</span>
</div>
),
};
}),
}}
/>
)}
</Section>
<System.DescriptionGroup
style={{ marginTop: 64, maxWidth: 688 }}
label="Miners"
description="Specify miners for our deal maker to try first, and specify miners for our deal maker to ignore."
/>
<Section
style={{ marginTop: 24, maxWidth: 688, minWidth: "auto" }}
buttons={[
{
name: "Add miner",
onClick: this._handleAddTrustedMiner,
},
]}
>
<System.Table
data={{
columns: [
{
key: "miner",
name: "Miner",
width: "100%",
},
],
rows: this.state.trustedMiners.map((miner) => {
return {
miner: (
<div css={STYLES_ROW} key={miner}>
<span css={STYLES_LEFT} target="_blank">
{miner}
</span>
<span
css={STYLES_RIGHT}
onClick={() => this._handleRemoveTrustedMiner(miner)}
>
<SVG.Dismiss height="16px" />
</span>
</div>
),
};
}),
}}
/>
</Section>
<Section
style={{ maxWidth: 688, minWidth: "auto" }}
buttons={[
{
name: "Exclude miner",
onClick: this._handleAddExcludedMiner,
},
]}
>
<System.Table
data={{
columns: [
{
key: "miner",
name: "Miner",
width: "100%",
},
],
rows: this.state.excludedMiners.map((miner) => {
return {
miner: (
<div css={STYLES_ROW} key={miner}>
<span css={STYLES_LEFT} target="_blank">
Excluding: {miner}
</span>
<span
css={STYLES_RIGHT}
onClick={() => this._handleRemoveExcludedMiner(miner)}
>
<SVG.Dismiss height="16px" />
</span>
</div>
),
};
}),
}}
/>
</Section>
<System.DescriptionGroup
style={{ marginTop: 64, maxWidth: 688 }}
label="Default Filecoin address"
description={`${this.state.addr}`}
/>
<System.Input
containerStyle={{ marginTop: 32, maxWidth: 688 }}
descriptionStyle={{ maxWidth: 688 }}
label="Default Filecoin replication and availability factor"
description="How many times should we replicate this deal across your selected miners?"
name="repFactor"
type="number"
value={this.state.repFactor}
placeholder="Type in amount of miners"
onChange={this._handleChange}
/>
<System.Input
containerStyle={{ marginTop: 24, maxWidth: 688 }}
descriptionStyle={{ maxWidth: 688 }}
label="Default Filecoin deal duration"
description={`Your deal is set for ${Strings.getDaysFromEpoch(
this.state.dealMinDuration
)}.`}
name="dealMinDuration"
type="number"
unit="epochs"
value={this.state.dealMinDuration}
placeholder="Type in epochs (1 epoch = ~30 seconds)"
onChange={this._handleChange}
/>
<System.Input
containerStyle={{ marginTop: 24, maxWidth: 688 }}
descriptionStyle={{ maxWidth: 688 }}
label="Max Filecoin price"
unit="attoFIL"
type="number"
description={`Set the maximum Filecoin price you're willing to pay. The current price you have set is equivalent to ${inFil} FIL`}
name="maxPrice"
value={this.state.maxPrice}
placeholder="Type in amount of Filecoin (attoFIL)"
onChange={this._handleChange}
/>
<System.CheckBox
style={{ marginTop: 48 }}
name="encryption"
value={this.state.encryption}
onChange={this._handleChange}
>
Encrypt this storage deal. Accessing the contents will require decryption.
</System.CheckBox>
<System.ButtonPrimary
style={{ marginTop: 48 }}
onClick={this._handleArchive}
loading={this.state.archiving}
>
Make storage deal
</System.ButtonPrimary>
</React.Fragment>
) : (
<div css={STYLES_SPINNER_CONTAINER}>
<LoaderSpinner style={{ height: 32, width: 32 }} />
</div>
)}
</ScenePage>
</WebsitePrototypeWrapper>
);
}
}

View File

@ -25,7 +25,15 @@ const createDealsTable = db.schema.createTable("deals", function (table) {
table.timestamp("createdAt").notNullable().defaultTo(db.raw("now()"));
});
Promise.all([createDealsTable]);
const deleteStorageDealSettings = db.schema.table("users", function (table) {
table.dropColumns(
"settingsDealsAutoApprove",
"allowEncryptedDataStorage",
"allowAutomaticDataStorage"
);
});
Promise.all([deleteStorageDealSettings]);
Logging.log(`FINISHED: adjust.js`);
Logging.log(` CTRL +C to return to terminal.`);

View File

@ -131,9 +131,6 @@ const addUserColumns = async () => {
// table.string("textileToken", 400).nullable();
// table.string("textileThreadID").nullable();
// table.string("textileBucketCID").nullable();
table.boolean("settingsDealsAutoApprove").notNullable().defaultTo(false);
table.boolean("allowAutomaticDataStorage").notNullable().defaultTo(true);
table.boolean("allowEncryptedDataStorage").notNullable().defaultTo(true);
table.jsonb("onboarding").nullable();
});
};
@ -197,10 +194,7 @@ const migrateUserTable = async () => {
name: data.name,
body: data.body,
photo: data.photo,
// textileKey: data.tokens?.api,
settingsDealsAutoApprove: data.settings?.settings_deals_auto_approve,
allowAutomaticDataStorage: data.settings?.allow_automatic_data_storage,
allowEncryptedDataStorage: data.settings?.allow_encrypted_data_storage,
textileKey: data.tokens?.api,
onboarding:
data.onboarding || data.status?.hidePrivacyAlert
? { ...data.onboarding, hidePrivacyAlert: data.status?.hidePrivacyAlert }
@ -349,9 +343,6 @@ Users
'data.photo', -> 'photo' MIGRATED
'data.status', -> 'onboarding.hidePrivacyAlert' MIGRATED
'data.tokens.api', -> 'textileKey' MIGRATED
'data.settings.settings_deals_auto_approve', -> 'settingsDealsAutoApprove' MIGRATED
'data.settings.allow_automatic_data_storage', -> 'allowAutomaticDataStorage' MIGRATED
'data.settings.allow_encrypted_data_storage', -> 'allowEncryptedDataStorage' MIGRATED
'data.onboarding', -> 'onboarding' MIGRATED
'data.twitter.username', -> 'twitterUsername' MIGRATED
'data.twitter.verified', -> 'twitterVerified' MIGRATED

View File

@ -43,9 +43,6 @@ const createUsersTable = db.schema.createTable("users", function (table) {
table.string("textileToken", 400).nullable();
table.string("textileThreadID").nullable();
table.string("textileBucketCID").nullable();
table.boolean("settingsDealsAutoApprove").notNullable().defaultTo(false);
table.boolean("allowAutomaticDataStorage").notNullable().defaultTo(true);
table.boolean("allowEncryptedDataStorage").notNullable().defaultTo(true);
table.jsonb("onboarding").nullable();
table.integer("followerCount").notNullable().defaultTo(0);
table.integer("slateCount").notNullable().defaultTo(0);

View File

@ -1,498 +0,0 @@
import configs from "~/knexfile";
import knex from "knex";
import fs from "fs-extra";
import "isomorphic-fetch";
import * as Logging from "~/common/logging";
import * as Environment from "~/node_common/environment";
import * as Data from "~/node_common/data";
import * as Utilities from "~/node_common/utilities";
import * as Strings from "~/common/strings";
import * as Logs from "~/node_common/script-logging";
import * as Filecoin from "~/common/filecoin";
import * as Serializers from "~/node_common/serializers";
import { Buckets, PrivateKey } from "@textile/hub";
import { v4 as uuid } from "uuid";
const envConfig = configs["development"];
const db = knex(envConfig);
// NOTE(jim): These essentially do the same thing.
// 1 GB to be considered to even make a deal.
const MINIMUM_BYTES_CONSIDERATION = 104857600 * 10;
// 1 GB minimum to make deal.
const MINIMUM_BYTES_FOR_STORAGE = 104857600 * 10;
const STORAGE_BOT_NAME = "STORAGE WORKER";
// We don't make new buckets if they have more than 10.
const BUCKET_LIMIT = 10;
const PRACTICE_RUN = true;
const SKIP_NEW_BUCKET_CREATION = false;
const STORE_MEANINGFUL_ADDRESS_ONLY_AND_PERFORM_NO_ACTIONS = false;
const WRITE_TO_SLATE_STORAGE_DEAL_INDEX = true;
const TEXTILE_KEY_INFO = {
key: Environment.TEXTILE_HUB_KEY,
secret: Environment.TEXTILE_HUB_SECRET,
};
Logging.log(`RUNNING: worker-heavy-stones.js`);
const delay = async (waitMs) => {
return await new Promise((resolve) => setTimeout(resolve, waitMs));
};
const minerMap = {};
const run = async () => {
Logs.taskTimeless(`Fetching every miner ...`);
const minerData = await fetch("https://sentinel.slate.host/api/mapped-static-global-miners");
const jsonData = await minerData.json();
jsonData.data.forEach((group) => {
group.minerAddresses.forEach((entity) => {
minerMap[entity.id] = entity;
minerMap[entity.id.replace("t", "f")] = entity;
});
});
Logs.taskTimeless(`Fetching every user ...`);
const response = await Data.getEveryUser();
let storageUsers = [];
let bytes = 0;
let dealUsers = 0;
let totalUsers = 0;
let encryptedUsers = 0;
// NOTE(jim): Only users who agree. Opt in by default.
for (let i = 0; i < response.length; i++) {
const user = response[i];
if (user.allowAutomaticDataStorage) {
storageUsers.unshift(user);
dealUsers = dealUsers + 1;
}
if (user.allowEncryptedDataStorage) {
encryptedUsers = encryptedUsers + 1;
}
totalUsers = totalUsers + 1;
}
for (let i = 0; i < storageUsers.length; i++) {
const user = storageUsers[i];
const printData = {
username: storageUsers[i].username,
slateURL: `https://slate.host/${storageUsers[i].username}`,
isForcingEncryption: user.allowEncryptedDataStorage,
};
let buckets;
await delay(500);
try {
const token = user.textileKey;
const identity = await PrivateKey.fromString(token);
buckets = await Buckets.withKeyInfo(TEXTILE_KEY_INFO);
await buckets.getToken(identity);
buckets = await Utilities.setupWithThread({ buckets });
} catch (e) {
Logs.error(e.message);
}
let userBuckets = [];
try {
userBuckets = await buckets.list();
} catch (e) {
Logs.error(e.message);
}
let userBytes = 0;
for (let k = 0; k < userBuckets.length; k++) {
try {
const path = await buckets.listPath(userBuckets[k].key, "/");
const data = path.item;
if (data.name !== "data") {
continue;
}
userBuckets[k].bucketSize = data.size;
userBytes = userBytes + data.size;
bytes = bytes + userBytes;
} catch (e) {
Logs.error(e.message);
}
}
// NOTE(jim): Skip people.
if (userBytes < MINIMUM_BYTES_CONSIDERATION) {
Logs.note(`SKIP: ${user.username}, they only have ${Strings.bytesToSize(userBytes)}`);
continue;
}
printData.bytes = userBytes;
const FilecoinSingleton = await Utilities.getFilecoinAPIFromUserToken({
user,
});
const { filecoin } = FilecoinSingleton;
let balance = 0;
let address = null;
await delay(500);
try {
const addresses = await filecoin.addresses();
addresses.forEach((a) => {
balance = a.balance;
address = a.address;
});
} catch (e) {
Logs.error(e.message);
}
let storageDeals = [];
try {
const records = await filecoin.storageDealRecords({
ascending: false,
includePending: false,
includeFinal: true,
});
records.forEach((o) => {
storageDeals.push({
dealId: o.dealInfo.dealId,
rootCid: o.rootCid,
proposalCid: o.dealInfo.proposalCid,
pieceCid: o.dealInfo.pieceCid,
addr: o.address,
size: o.dealInfo.size,
// NOTE(jim): formatted size.
formattedSize: Strings.bytesToSize(o.dealInfo.size),
pricePerEpoch: o.dealInfo.pricePerEpoch,
startEpoch: o.dealInfo.startEpoch,
// NOTE(jim): just for point of reference on the total cost.
totalCostFIL: Filecoin.formatAsFilecoinConversion(
o.dealInfo.pricePerEpoch * o.dealInfo.duration
),
totalCostAttoFIL: o.dealInfo.pricePerEpoch * o.dealInfo.duration,
duration: o.dealInfo.duration,
formattedDuration: Strings.getDaysFromEpoch(o.dealInfo.duration),
activationEpoch: o.dealInfo.activationEpoch,
time: o.time,
pending: o.pending,
createdAt: Strings.toDateSinceEpoch(o.time),
userEncryptsDeals: !!user.allowEncryptedDataStorage,
miner: minerMap[o.dealInfo.miner] ? minerMap[o.dealInfo.miner] : { id: o.dealInfo.miner },
phase: "MARCH",
user: {
id: user.id,
username: user.username,
photo: user.photo,
name: user.name,
},
});
});
} catch (e) {
Logs.error(e.message);
}
printData.address = address;
printData.balanceAttoFil = balance;
Logs.taskTimeless(`\x1b[36m\x1b[1mhttps://slate.host/${user.username}\x1b[0m`);
Logs.taskTimeless(`\x1b[36m\x1b[1m${address}\x1b[0m`);
Logs.taskTimeless(`\x1b[36m\x1b[1m${Strings.bytesToSize(userBytes)} stored each deal.\x1b[0m`);
Logs.taskTimeless(
`\x1b[36m\x1b[1m${Filecoin.formatAsFilecoinConversion(balance)} remaining\x1b[0m`
);
// NOTE(jim): Anyone can get a list for storage deals from Slate.
if (WRITE_TO_SLATE_STORAGE_DEAL_INDEX) {
const hasDealId = (id) => db.raw(`?? @> ?::jsonb`, ["data", JSON.stringify({ dealId: id })]);
for (let d = 0; d < storageDeals.length; d++) {
const dealToSave = storageDeals[d];
Logs.note(`Saving ${dealToSave.dealId} ...`);
Logging.log(dealToSave);
const existing = await db.select("*").from("deals").where(hasDealId(dealToSave.dealId));
Logging.log(existing);
if (existing && !existing.error && existing.length) {
Logs.error(`${dealToSave.dealId} is already saved.`);
continue;
}
Logs.note(`Inserting ${dealToSave.dealId} ...`);
await delay(1000);
await db.insert({ data: dealToSave, ownerId: user.id }).into("deals").returning("*");
Logs.task(`Inserted ${dealToSave.dealId} !!!`);
}
}
// NOTE(jim): Exit early for analytics purposes.
if (STORE_MEANINGFUL_ADDRESS_ONLY_AND_PERFORM_NO_ACTIONS) {
Logs.taskTimeless(`Adding address for: ${user.username}`);
continue;
}
// NOTE(jim): Skip users that are out of funds.
if (balance === 0) {
Logs.error(`OUT OF FUNDS: ${user.username}`);
continue;
}
// NOTE(jim): tracks all buckets.
printData.buckets = [];
for (let j = 0; j < userBuckets.length; j++) {
const keyBucket = userBuckets[j];
let key;
let encrypt;
if (keyBucket.name.startsWith("open-")) {
Logs.note(`bucket found: open-data ${keyBucket.key}`);
Logs.note(`checking size ...`);
let bucketSizeBytes = null;
try {
const path = await buckets.listPath(keyBucket.key, "/");
bucketSizeBytes = path.item.size;
} catch (e) {
Logs.error(e.message);
continue;
}
// NOTE(jim): Determine open deals
try {
const { current, history } = await buckets.archives(keyBucket.key);
Logging.log(current);
Logging.log(history);
} catch (e) {
Logs.error(e.message);
continue;
}
if (bucketSizeBytes && bucketSizeBytes < MINIMUM_BYTES_FOR_STORAGE) {
try {
Logs.error(`we must kill this bucket ...`);
await buckets.remove(keyBucket.key);
Logs.note(`bucket removed ...`);
} catch (e) {
Logs.error(e.message);
continue;
}
}
if (bucketSizeBytes && bucketSizeBytes >= MINIMUM_BYTES_FOR_STORAGE) {
Logs.task(`bucket is okay and fits requirements !!!`);
key = keyBucket.key;
}
}
if (keyBucket.name.startsWith("encrypted-data-")) {
Logs.note(`bucket found: encrypted-data ${keyBucket.key}`);
Logs.note(`checking size ...`);
let bucketSizeBytes = null;
try {
const path = await buckets.listPath(keyBucket.key, "/");
bucketSizeBytes = path.item.size;
} catch (e) {
Logs.error(e.message);
continue;
}
// NOTE(jim): Determine open deals
try {
const { current, history } = await buckets.archives(keyBucket.key);
Logging.log(current);
Logging.log(history);
} catch (e) {
Logs.error(e.message);
continue;
}
if (bucketSizeBytes && bucketSizeBytes < MINIMUM_BYTES_FOR_STORAGE) {
try {
Logs.error(`we must kill this bucket ...`);
await buckets.remove(keyBucket.key);
Logs.note(`bucket removed ...`);
} catch (e) {
Logs.error(e.message);
continue;
}
}
if (bucketSizeBytes && bucketSizeBytes >= MINIMUM_BYTES_FOR_STORAGE) {
Logs.task(`bucket is okay and fits requirements !!!`);
key = keyBucket.key;
}
}
// NOTE(jim): Temporarily prevent more buckets from being created for legacy accounts.
if (
keyBucket.name === "data" &&
!SKIP_NEW_BUCKET_CREATION &&
userBuckets.length < BUCKET_LIMIT
) {
key = null;
encrypt = !!user.allowEncryptedDataStorage;
// NOTE(jim): Create a new bucket
const newBucketName = encrypt ? `encrypted-data-${uuid()}` : `open-data-${uuid()}`;
// NOTE(jim): Get the root key of the bucket
let bucketSizeBytes = null;
let items;
try {
const path = await buckets.listPath(keyBucket.key, "/");
items = path.item;
bucketSizeBytes = path.item.size;
} catch (e) {
Logs.error(e.message);
}
if (bucketSizeBytes && bucketSizeBytes < MINIMUM_BYTES_FOR_STORAGE) {
Logs.error(`Root 'data' bucket does not fit size requirements. Skipping.`);
continue;
}
await delay(1000);
Logs.task(`creating new bucket: ${newBucketName}.`);
// NOTE(jim): Create a new bucket
try {
Logs.note(`attempting ... `);
if (!PRACTICE_RUN) {
Logs.note(`name: ${newBucketName} ...`);
Logs.note(`cid: ${items.cid} ...`);
let newBucket = await buckets.create(newBucketName, encrypt, items.cid);
key = newBucket.root.key;
Logs.task(`created ${newBucketName} successfully with new key ${key}.`);
} else {
Logs.note(`practice skipping ...`);
continue;
}
} catch (e) {
Logs.error(e.message);
}
await delay(5000);
}
if (key) {
await delay(500);
try {
if (!PRACTICE_RUN) {
// NOTE(jim): THE DEAL HAPPENS HERE
// DON'T DO IT IF YOU DON'T WANT THE DEAL
Logs.task(`KICKING OFF THE DEAL`);
await buckets.archive(key, {
repFactor: 4,
dealMinDuration: 518400,
excludedMiners: null,
trustedMiners: [
// NOTE(jim): ChainSafe
// f01247 belongs to ChainSafe, they have white-list rule, you need to ask them add your address
"f01247",
"f01278",
"f071624",
"f0135078",
"f09848",
"f010617",
"f01276",
"f02401",
"f02387",
"f019104",
"f014409",
"f066596",
"f058369",
"f08399",
"f015927",
],
countryCodes: null,
renew: {
enabled: false,
threshold: 0,
},
maxPrice: 192901234500,
fastRetrieval: true,
dealStartOffset: 8640,
});
Logs.task(`\x1b[32mNEW DEAL SUCCESSFUL !!!\x1b[0m`);
} else {
Logs.note(`archive skipping ...`);
}
printData.buckets.push({
key,
success: false,
});
} catch (e) {
if (e.message === `the same bucket cid is already archived successfully`) {
printData.buckets.push({
key,
success: true,
});
} else {
printData.buckets.push({
key,
success: false,
});
}
Logs.note(e.message);
}
}
}
for (let k = 0; k < printData.buckets.length; k++) {
let targetBucket = printData.buckets[k];
Logs.task(`Show us the history!`);
try {
const { current, history } = await buckets.archives(targetBucket.key);
Logging.log(current);
Logging.log(history);
} catch (e) {
Logs.error(e.message);
continue;
}
if (targetBucket.success) {
try {
Logs.task(`deleting bucket with key: ${targetBucket.key}`);
await buckets.remove(targetBucket.key);
Logs.task(`successfully deleted ${targetBucket.key}`);
} catch (e) {
Logs.error(e.message);
}
}
}
Logging.log("\n");
}
Logs.task(`total storage per run: ${Strings.bytesToSize(bytes)}`);
Logs.task(`total storage per run (with replication x5): ${Strings.bytesToSize(bytes * 5)}`);
Logs.task(`creating slate-storage-addresses.json`);
Logging.log(`${STORAGE_BOT_NAME} finished. \n\n`);
Logging.log(`FINISHED: worker-heavy-stones.js`);
Logging.log(` CTRL +C to return to terminal.`);
};
run();

View File

@ -195,9 +195,13 @@ const run = async (props) => {
.whereRaw('users.id = "files"."ownerId"')
.where("files.isLink", false);
})
.limit(1);
.whereNotExists(function () {
this.select("id")
.from("deals")
.whereRaw('"users"."textileBucketCID" = "deals"."textileBucketCID"');
});
for (let user of users) {
if (i % 100 === 0) {
if (i % 500 === 0) {
console.log(i);
if (successful.length) {
await DB.insert(successful).into("deals");
@ -219,6 +223,7 @@ const run = async (props) => {
if (successful.length) {
await DB.insert(successful).into("deals");
}
console.log("SCRIPT FINISHED");
};
const addToEstuary = async (cid) => {