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

Reject dataset uploads if organization storage quota is exceeded #6893

Merged
merged 6 commits into from
Mar 27, 2023
Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released
- Interpolation during rendering is now more performance intensive, since the rendering approach was changed. Therefore, interpolation is disabled by default. On the flip side, the rendered quality is often higher than it used to be. [#6748](https://github.com/scalableminds/webknossos/pull/6748)
- Updated the styling of the "welcome" screen for new users to be in line with the new branding. [#6904](https://github.com/scalableminds/webknossos/pull/6904)
- Improved Terms-of-Service modal (e.g., allow to switch organization even when modal was blocking the remaining usage of WEBKNOSSOS). [#6930](https://github.com/scalableminds/webknossos/pull/6930)
- Uploads are now blocked when the organization’s storage quota is exceeded. [#6893](https://github.com/scalableminds/webknossos/pull/6893)

### Fixed
- Fixed an issue with text hints not being visible on the logout page for dark mode users. [#6916](https://github.com/scalableminds/webknossos/pull/6916)
Expand Down
3 changes: 3 additions & 0 deletions app/controllers/WKRemoteDataStoreController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ class WKRemoteDataStoreController @Inject()(
organization <- organizationDAO.findOneByName(uploadInfo.organization)(GlobalAccessContext) ?~> Messages(
"organization.notFound",
uploadInfo.organization) ~> NOT_FOUND
usedStorageBytes <- organizationDAO.getUsedStorage(organization._id)
_ <- Fox.runOptional(organization.includedStorageBytes)(includedStorage =>
bool2Fox(usedStorageBytes <= includedStorage)) ?~> "dataSet.upload.storageExceeded" ~> FORBIDDEN
_ <- bool2Fox(organization._id == user._organization) ?~> "notAllowed" ~> FORBIDDEN
_ <- dataSetService.assertValidDataSetName(uploadInfo.name)
_ <- dataSetService.assertNewDataSetName(uploadInfo.name, organization._id) ?~> "dataSet.name.alreadyTaken"
Expand Down
1 change: 1 addition & 0 deletions conf/messages
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ dataSet.upload.validation.failed=Failed to validate Dataset information for uplo
dataSet.upload.linkRestricted=Can only link layers of datasets that are either public or allowed to be administrated by your account
dataSet.upload.invalidLinkedLayers=Could not link all requested layers
dataSet.upload.noFiles=Tried to finish upload with no files. May be a retry of a failed finish request, see previous errors.
dataSet.upload.storageExceeded=Cannot upload dataset because the storage quota of the organization is exceeded.
dataSet.explore.failed.readFile=Failed to read remote file
dataSet.explore.magDtypeMismatch=Element class must be the same for all mags of a layer. Got {0}

Expand Down
32 changes: 30 additions & 2 deletions frontend/javascripts/admin/dataset/dataset_upload_view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,15 @@ import classnames from "classnames";
import _ from "lodash";
import { useDropzone, FileWithPath } from "react-dropzone";
import ErrorHandling from "libs/error_handling";
import type { RouteComponentProps } from "react-router-dom";
import { Link, RouteComponentProps } from "react-router-dom";
import { withRouter } from "react-router-dom";
import type { APITeam, APIDataStore, APIUser, APIDatasetId } from "types/api_flow_types";
import type {
APITeam,
APIDataStore,
APIUser,
APIDatasetId,
APIOrganization,
} from "types/api_flow_types";
import type { OxalisState } from "oxalis/store";
import {
reserveDatasetUpload,
Expand Down Expand Up @@ -41,6 +47,8 @@ import { FormInstance } from "antd/lib/form";
import type { Vector3 } from "oxalis/constants";
import { FormItemWithInfo, confirmAsync } from "../../dashboard/dataset/helper_components";
import FolderSelection from "dashboard/folders/folder_selection";
import { hasPricingPlanExceededStorage } from "admin/organization/pricing_plan_utils";
import { enforceActiveOrganization } from "oxalis/model/accessors/organization_accessors";

const FormItem = Form.Item;
const REPORT_THROTTLE_THRESHOLD = 1 * 60 * 1000; // 1 min
Expand All @@ -56,6 +64,7 @@ type OwnProps = {
};
type StateProps = {
activeUser: APIUser | null | undefined;
organization: APIOrganization;
};
type Props = OwnProps & StateProps;
type PropsWithFormAndRouter = Props & {
Expand Down Expand Up @@ -613,6 +622,23 @@ class DatasetUploadView extends React.Component<PropsWithFormAndRouter, State> {
}}
>
<CardContainer withoutCard={withoutCard} title="Upload Dataset">
{hasPricingPlanExceededStorage(this.props.organization) ? (
<Alert
type="error"
message={
<>
Your organization has exceeded the available storage. Uploading new datasets is
disabled. Visit the{" "}
<Link to={`/organizations/${this.props.organization.name}`}>
organization page
</Link>{" "}
for details.
</>
}
style={{ marginBottom: 8 }}
/>
) : null}

<Form
onFinish={this.handleSubmit}
layout="vertical"
Expand Down Expand Up @@ -856,6 +882,7 @@ class DatasetUploadView extends React.Component<PropsWithFormAndRouter, State> {
size="large"
type="primary"
htmlType="submit"
disabled={hasPricingPlanExceededStorage(this.props.organization)}
style={{
width: "100%",
}}
Expand Down Expand Up @@ -1043,6 +1070,7 @@ function FileUploadArea({

const mapStateToProps = (state: OxalisState): StateProps => ({
activeUser: state.activeUser,
organization: enforceActiveOrganization(state.activeOrganization),
});

const connector = connect(mapStateToProps);
Expand Down