Compare commits

...

2 Commits

Author SHA1 Message Date
Alex
7dffd5f079 Release v0.43.1 (#3161) 2023-12-18 17:00:58 -06:00
Cesar N
044e5702df Update Share Object UI to reflect max expiration time (#3098) 2023-12-14 15:28:11 -06:00
19 changed files with 154 additions and 33 deletions

View File

@@ -2,6 +2,12 @@
# Changelog
## Release v0.43.1
Bug Fix:
- Update Share Object UI to reflect maximum expiration time in UI
## Release v0.43.0
Features:

View File

@@ -1,7 +1,7 @@
{
"files": {
"main.css": "./static/css/main.e60e4760.css",
"main.js": "./static/js/main.bfa676cc.js",
"main.js": "./static/js/main.f92800f2.js",
"static/js/1260.291b50cb.chunk.js": "./static/js/1260.291b50cb.chunk.js",
"static/js/6914.9c5fdb44.chunk.js": "./static/js/6914.9c5fdb44.chunk.js",
"static/js/9121.1e573e4c.chunk.js": "./static/js/9121.1e573e4c.chunk.js",
@@ -40,7 +40,7 @@
"static/js/6577.d820c471.chunk.js": "./static/js/6577.d820c471.chunk.js",
"static/js/3875.458718d4.chunk.js": "./static/js/3875.458718d4.chunk.js",
"static/js/3115.3c5e3052.chunk.js": "./static/js/3115.3c5e3052.chunk.js",
"static/js/2680.76c11a81.chunk.js": "./static/js/2680.76c11a81.chunk.js",
"static/js/4646.aa9a58d3.chunk.js": "./static/js/4646.aa9a58d3.chunk.js",
"static/js/977.a3dd8fca.chunk.js": "./static/js/977.a3dd8fca.chunk.js",
"static/js/6686.01ae5612.chunk.js": "./static/js/6686.01ae5612.chunk.js",
"static/js/9059.25ffd18e.chunk.js": "./static/js/9059.25ffd18e.chunk.js",
@@ -119,7 +119,7 @@
"static/media/placeholderimage.png": "./static/media/placeholderimage.077ea48bd1ef1f4a883f.png",
"index.html": "./index.html",
"main.e60e4760.css.map": "./static/css/main.e60e4760.css.map",
"main.bfa676cc.js.map": "./static/js/main.bfa676cc.js.map",
"main.f92800f2.js.map": "./static/js/main.f92800f2.js.map",
"1260.291b50cb.chunk.js.map": "./static/js/1260.291b50cb.chunk.js.map",
"6914.9c5fdb44.chunk.js.map": "./static/js/6914.9c5fdb44.chunk.js.map",
"9121.1e573e4c.chunk.js.map": "./static/js/9121.1e573e4c.chunk.js.map",
@@ -158,7 +158,7 @@
"6577.d820c471.chunk.js.map": "./static/js/6577.d820c471.chunk.js.map",
"3875.458718d4.chunk.js.map": "./static/js/3875.458718d4.chunk.js.map",
"3115.3c5e3052.chunk.js.map": "./static/js/3115.3c5e3052.chunk.js.map",
"2680.76c11a81.chunk.js.map": "./static/js/2680.76c11a81.chunk.js.map",
"4646.aa9a58d3.chunk.js.map": "./static/js/4646.aa9a58d3.chunk.js.map",
"977.a3dd8fca.chunk.js.map": "./static/js/977.a3dd8fca.chunk.js.map",
"6686.01ae5612.chunk.js.map": "./static/js/6686.01ae5612.chunk.js.map",
"9059.25ffd18e.chunk.js.map": "./static/js/9059.25ffd18e.chunk.js.map",
@@ -217,6 +217,6 @@
},
"entrypoints": [
"static/css/main.e60e4760.css",
"static/js/main.bfa676cc.js"
"static/js/main.f92800f2.js"
]
}

View File

@@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><base href="/"/><meta content="width=device-width,initial-scale=1" name="viewport"/><meta content="#081C42" media="(prefers-color-scheme: light)" name="theme-color"/><meta content="#081C42" media="(prefers-color-scheme: dark)" name="theme-color"/><meta content="MinIO Console" name="description"/><meta name="minio-license" content="apgl"/><link href="./styles/root-styles.css" rel="stylesheet"/><link href="./apple-icon-180x180.png" rel="apple-touch-icon" sizes="180x180"/><link href="./favicon-32x32.png" rel="icon" sizes="32x32" type="image/png"/><link href="./favicon-96x96.png" rel="icon" sizes="96x96" type="image/png"/><link href="./favicon-16x16.png" rel="icon" sizes="16x16" type="image/png"/><link href="./manifest.json" rel="manifest"/><link color="#3a4e54" href="./safari-pinned-tab.svg" rel="mask-icon"/><title>MinIO Console</title><script defer="defer" src="./static/js/main.bfa676cc.js"></script><link href="./static/css/main.e60e4760.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"><div id="preload"><img src="./images/background.svg"/> <img src="./images/background-wave-orig2.svg"/></div><div id="loader-block"><img src="./Loader.svg"/></div></div></body></html>
<!doctype html><html lang="en"><head><meta charset="utf-8"/><base href="/"/><meta content="width=device-width,initial-scale=1" name="viewport"/><meta content="#081C42" media="(prefers-color-scheme: light)" name="theme-color"/><meta content="#081C42" media="(prefers-color-scheme: dark)" name="theme-color"/><meta content="MinIO Console" name="description"/><meta name="minio-license" content="apgl"/><link href="./styles/root-styles.css" rel="stylesheet"/><link href="./apple-icon-180x180.png" rel="apple-touch-icon" sizes="180x180"/><link href="./favicon-32x32.png" rel="icon" sizes="32x32" type="image/png"/><link href="./favicon-96x96.png" rel="icon" sizes="96x96" type="image/png"/><link href="./favicon-16x16.png" rel="icon" sizes="16x16" type="image/png"/><link href="./manifest.json" rel="manifest"/><link color="#3a4e54" href="./safari-pinned-tab.svg" rel="mask-icon"/><title>MinIO Console</title><script defer="defer" src="./static/js/main.f92800f2.js"></script><link href="./static/css/main.e60e4760.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"><div id="preload"><img src="./images/background.svg"/> <img src="./images/background-wave-orig2.svg"/></div><div id="loader-block"><img src="./Loader.svg"/></div></div></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -251,6 +251,36 @@ export const erasureCodeCalc = (
};
};
// 92400 seconds -> 1 day, 1 hour, 40 minutes.
export const niceTimeFromSeconds = (seconds: number): string => {
const days = Math.floor(seconds / (3600 * 24));
const hours = Math.floor((seconds % (3600 * 24)) / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const remainingSeconds = seconds % 60;
const parts = [];
if (days > 0) {
parts.push(`${days} day${days !== 1 ? "s" : ""}`);
}
if (hours > 0) {
parts.push(`${hours} hour${hours !== 1 ? "s" : ""}`);
}
if (minutes > 0) {
parts.push(`${minutes} minute${minutes !== 1 ? "s" : ""}`);
}
if (remainingSeconds > 0) {
parts.push(
`${remainingSeconds} second${remainingSeconds !== 1 ? "s" : ""}`,
);
}
return parts.join(" and ");
};
// seconds / minutes /hours / Days / Years calculator
export const niceDays = (secondsValue: string, timeVariant: string = "s") => {
let seconds = parseFloat(secondsValue);
@@ -258,6 +288,7 @@ export const niceDays = (secondsValue: string, timeVariant: string = "s") => {
return niceDaysInt(seconds, timeVariant);
};
// niceDaysInt returns the string in the max unit found e.g. 92400 seconds -> 1 day
export const niceDaysInt = (seconds: number, timeVariant: string = "s") => {
switch (timeVariant) {
case "ns":

View File

@@ -31,7 +31,7 @@ const initialState: BucketDetailsState = {
};
export const bucketDetailsSlice = createSlice({
name: "trace",
name: "bucketDetails",
initialState,
reducers: {
setBucketDetailsTab: (state, action: PayloadAction<string>) => {

View File

@@ -16,11 +16,22 @@
import React, { Fragment, useEffect, useState } from "react";
import { useSelector } from "react-redux";
import { Button, CopyIcon, ReadBox, ShareIcon, Grid, ProgressBar } from "mds";
import {
Button,
CopyIcon,
ReadBox,
ShareIcon,
Grid,
ProgressBar,
Tooltip,
} from "mds";
import CopyToClipboard from "react-copy-to-clipboard";
import ModalWrapper from "../../../../Common/ModalWrapper/ModalWrapper";
import DaysSelector from "../../../../Common/FormComponents/DaysSelector/DaysSelector";
import { encodeURLString } from "../../../../../../common/utils";
import {
encodeURLString,
niceTimeFromSeconds,
} from "../../../../../../common/utils";
import {
selDistSet,
setModalErrorSnackMessage,
@@ -30,6 +41,8 @@ import { useAppDispatch } from "../../../../../../store";
import { BucketObject } from "api/consoleApi";
import { api } from "api";
import { errorToHandler } from "api/errors";
import { getMaxShareLinkExpTime } from "screens/Console/ObjectBrowser/objectBrowserThunks";
import { maxShareLinkExpTime } from "screens/Console/ObjectBrowser/objectBrowserSlice";
interface IShareFileProps {
open: boolean;
@@ -46,6 +59,7 @@ const ShareFile = ({
}: IShareFileProps) => {
const dispatch = useAppDispatch();
const distributedSetup = useSelector(selDistSet);
const maxshareLinkExpTimeVal = useSelector(maxShareLinkExpTime);
const [shareURL, setShareURL] = useState<string>("");
const [isLoadingVersion, setIsLoadingVersion] = useState<boolean>(true);
const [isLoadingFile, setIsLoadingFile] = useState<boolean>(false);
@@ -65,6 +79,10 @@ const ShareFile = ({
setShareURL("");
};
useEffect(() => {
dispatch(getMaxShareLinkExpTime());
}, [dispatch]);
useEffect(() => {
// In case version is undefined, we get the latest version of the object
if (dataObject.version_id === undefined) {
@@ -172,11 +190,28 @@ const ShareFile = ({
fontWeight: 400,
}}
>
This is a temporary URL with integrated access credentials for
sharing objects valid for up to 7 days.
<br />
<br />
The temporary URL expires after the configured time limit.
<Tooltip
placement="right"
tooltip={
<span>
You can reset your session by logging out and logging back
in to the web UI. <br /> <br />
You can increase the maximum configuration time by setting
the MINIO_STS_DURATION environment variable on all your
nodes. <br /> <br />
You can use <b>mc share</b> as an alternative to this UI,
where the session length does not limit the URL validity.
</span>
}
>
<span>
The following URL lets you share this object without requiring
a login. <br />
The URL expires automatically at the earlier of your
configured time ({niceTimeFromSeconds(maxshareLinkExpTimeVal)}
) or the expiration of your current web session.
</span>
</Tooltip>
</Grid>
<br />
<Grid item xs={12}>
@@ -184,7 +219,7 @@ const ShareFile = ({
initialDate={initialDate}
id="date"
label="Active for"
maxDays={7}
maxSeconds={maxshareLinkExpTimeVal}
onChange={dateChanged}
entity="Link"
/>

View File

@@ -18,10 +18,14 @@ import React, { useEffect, useState } from "react";
import { DateTime } from "luxon";
import { Box, InputBox, InputLabel, LinkIcon } from "mds";
const DAY_SECONDS = 86400;
const HOUR_SECONDS = 3600;
const HOUR_MINUTES = 60;
interface IDaysSelector {
id: string;
initialDate: Date;
maxDays?: number;
maxSeconds: number;
label: string;
entity: string;
onChange: (newDate: string, isValid: boolean) => void;
@@ -43,16 +47,27 @@ const DaysSelector = ({
id,
initialDate,
label,
maxDays,
maxSeconds,
entity,
onChange,
}: IDaysSelector) => {
const [selectedDays, setSelectedDays] = useState<number>(7);
const maxDays = Math.floor(maxSeconds / DAY_SECONDS);
const maxHours = Math.floor((maxSeconds % DAY_SECONDS) / HOUR_SECONDS);
const maxMinutes = Math.floor((maxSeconds % HOUR_SECONDS) / HOUR_MINUTES);
const [selectedDays, setSelectedDays] = useState<number>(0);
const [selectedHours, setSelectedHours] = useState<number>(0);
const [selectedMinutes, setSelectedMinutes] = useState<number>(0);
const [validDate, setValidDate] = useState<boolean>(true);
const [dateSelected, setDateSelected] = useState<DateTime>(DateTime.now());
// Set initial values
useEffect(() => {
setSelectedDays(maxDays);
setSelectedHours(maxHours);
setSelectedMinutes(maxMinutes);
}, [maxDays, maxHours, maxMinutes]);
useEffect(() => {
if (
!isNaN(selectedHours) &&
@@ -82,9 +97,11 @@ const DaysSelector = ({
// Basic validation for inputs
useEffect(() => {
let valid = true;
if (
selectedDays < 0 ||
(maxDays && selectedDays > maxDays) ||
selectedDays > 7 ||
selectedDays > maxDays ||
isNaN(selectedDays)
) {
valid = false;
@@ -98,12 +115,16 @@ const DaysSelector = ({
valid = false;
}
if (
maxDays &&
selectedDays === maxDays &&
(selectedHours !== 0 || selectedMinutes !== 0)
) {
valid = false;
if (selectedDays === maxDays) {
if (selectedHours > maxHours) {
valid = false;
}
if (selectedHours === maxHours) {
if (selectedMinutes > maxMinutes) {
valid = false;
}
}
}
if (selectedDays <= 0 && selectedHours <= 0 && selectedMinutes <= 0) {
@@ -114,6 +135,8 @@ const DaysSelector = ({
}, [
dateSelected,
maxDays,
maxHours,
maxMinutes,
onChange,
selectedDays,
selectedHours,
@@ -165,7 +188,7 @@ const DaysSelector = ({
className={`reverseInput removeArrows`}
type="number"
min="0"
max={maxDays ? maxDays.toString() : "999"}
max="7"
label="Days"
name={id}
onChange={(e) => {

View File

@@ -24,6 +24,7 @@ import {
BucketVersioningResponse,
GetBucketRetentionConfig,
} from "api/consoleApi";
import { AppState } from "store";
const defaultRewind = {
rewindEnabled: false,
@@ -76,6 +77,7 @@ const initialState: ObjectBrowserState = {
validity: 0,
},
longFileOpen: false,
maxShareLinkExpTime: 0,
};
export const objectBrowserSlice = createSlice({
@@ -371,6 +373,9 @@ export const objectBrowserSlice = createSlice({
setAnonymousAccessOpen: (state, action: PayloadAction<boolean>) => {
state.anonymousAccessOpen = action.payload;
},
setMaxShareLinkExpTime: (state, action: PayloadAction<number>) => {
state.maxShareLinkExpTime = action.payload;
},
errorInConnection: (state, action: PayloadAction<boolean>) => {
state.connectionError = action.payload;
if (action.payload) {
@@ -425,7 +430,11 @@ export const {
setSelectedBucket,
setLongFileOpen,
setAnonymousAccessOpen,
setMaxShareLinkExpTime,
errorInConnection,
} = objectBrowserSlice.actions;
export const maxShareLinkExpTime = (state: AppState) =>
state.objectBrowser.maxShareLinkExpTime;
export default objectBrowserSlice.reducer;

View File

@@ -29,6 +29,7 @@ import {
failObject,
setAnonymousAccessOpen,
setDownloadRenameModal,
setMaxShareLinkExpTime,
setNewObject,
setPreviewOpen,
setSelectedPreview,
@@ -37,6 +38,7 @@ import {
} from "./objectBrowserSlice";
import { setSnackBarMessage } from "../../../systemSlice";
import { DateTime } from "luxon";
import { api } from "api";
export const downloadSelected = createAsyncThunk(
"objectBrowser/downloadSelected",
@@ -203,3 +205,17 @@ export const openAnonymousAccess = createAsyncThunk(
}
},
);
export const getMaxShareLinkExpTime = createAsyncThunk(
"objectBrowser/maxShareLinkExpTime",
async (_, { rejectWithValue, dispatch }) => {
return api.buckets
.getMaxShareLinkExp()
.then((res) => {
dispatch(setMaxShareLinkExpTime(res.data.exp));
})
.catch(async (res) => {
return rejectWithValue(res.error);
});
},
);

View File

@@ -57,6 +57,7 @@ export interface ObjectBrowserState {
longFileOpen: boolean;
anonymousAccessOpen: boolean;
connectionError: boolean;
maxShareLinkExpTime: number;
}
export interface ObjectManager {