Merged PR 440: 画面実装(テンプレートファイルアップロードPopup)
## 概要 [Task2656: 画面実装(テンプレートファイルアップロードPopup)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2656) - テンプレートファイルアップロードのAPI呼び出し周りを実装 - SASトークン付きURL取得 - Blobストレージへファイルアップロード - アップロード完了 - server側 - `helmet`の`connect-src`を修正 - SASトークン付きURLが想定と違っていたため修正 - DBに保存するURLが想定と違っていたため修正 ## レビューポイント - `connect-src`の`self`以外はローカル環境のみの設定でよさそう? - Popupの挙動で不足している箇所はあるか - アップロードファイルでチェックすべき内容等 ## UIの変更 - https://ndstokyo.sharepoint.com/:f:/r/sites/Piranha/Shared%20Documents/General/OMDS/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88/Task2656?csf=1&web=1&e=iU1huG ## 動作確認状況 - ローカルで確認 ## 補足 - 相談、参考資料などがあれば
This commit is contained in:
parent
bf4dc1d717
commit
8265ca38c8
1096
dictation_client/package-lock.json
generated
1096
dictation_client/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -19,6 +19,7 @@
|
||||
"dependencies": {
|
||||
"@azure/msal-browser": "^2.33.0",
|
||||
"@azure/msal-react": "^1.5.3",
|
||||
"@azure/storage-blob": "^12.16.0",
|
||||
"@reduxjs/toolkit": "^1.8.3",
|
||||
"@testing-library/jest-dom": "^5.16.4",
|
||||
"@testing-library/react": "^13.3.0",
|
||||
@ -51,6 +52,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.18.6",
|
||||
"@esbuild-plugins/node-modules-polyfill": "^0.2.2",
|
||||
"@mdx-js/react": "^2.1.2",
|
||||
"@openapitools/openapi-generator-cli": "^2.5.2",
|
||||
"@types/lodash": "^4.14.191",
|
||||
|
||||
@ -1,2 +1,3 @@
|
||||
// TODO 仮で5MBにしているが、OMDS様からの回答待ち
|
||||
// アップロード可能なファイルサイズの上限(MB)
|
||||
export const UPLOAD_FILE_SIZE_LIMIT: number = 5 * 1024 * 1024;
|
||||
|
||||
@ -1,9 +1,15 @@
|
||||
import { createAsyncThunk } from "@reduxjs/toolkit";
|
||||
import { Configuration, GetTemplatesResponse, TemplatesApi } from "api";
|
||||
import {
|
||||
Configuration,
|
||||
FilesApi,
|
||||
GetTemplatesResponse,
|
||||
TemplatesApi,
|
||||
} from "api";
|
||||
import type { RootState } from "app/store";
|
||||
import { ErrorObject, createErrorObject } from "common/errors";
|
||||
import { openSnackbar } from "features/ui/uiSlice";
|
||||
import { getTranslationID } from "translation";
|
||||
import { BlockBlobClient, ContainerClient } from "@azure/storage-blob";
|
||||
|
||||
export const listTemplateAsync = createAsyncThunk<
|
||||
GetTemplatesResponse,
|
||||
@ -40,3 +46,69 @@ export const listTemplateAsync = createAsyncThunk<
|
||||
return thunkApi.rejectWithValue({ error });
|
||||
}
|
||||
});
|
||||
|
||||
export const uploadTemplateAsync = createAsyncThunk<
|
||||
{
|
||||
/* Empty Object */
|
||||
},
|
||||
void,
|
||||
{
|
||||
// rejectした時の返却値の型
|
||||
rejectValue: {
|
||||
error: ErrorObject;
|
||||
};
|
||||
}
|
||||
>("workflow/uploadTemplateAsync", async (args, thunkApi) => {
|
||||
// apiのConfigurationを取得する
|
||||
const { getState } = thunkApi;
|
||||
const state = getState() as RootState;
|
||||
const { configuration, accessToken } = state.auth;
|
||||
const { uploadFile } = state.template.apps;
|
||||
const config = new Configuration(configuration);
|
||||
const filesApi = new FilesApi(config);
|
||||
|
||||
try {
|
||||
if (!uploadFile) {
|
||||
throw new Error("uploadFile is not found");
|
||||
}
|
||||
// SAS付きのURLを取得する
|
||||
const { data } = await filesApi.uploadTemplateLocation({
|
||||
headers: { authorization: `Bearer ${accessToken}` },
|
||||
});
|
||||
const { url } = data;
|
||||
|
||||
// ファイルをアップロードする
|
||||
const containerClient = new ContainerClient(url);
|
||||
const blockBlobClient: BlockBlobClient = containerClient.getBlockBlobClient(
|
||||
uploadFile.name
|
||||
);
|
||||
await blockBlobClient.uploadData(uploadFile);
|
||||
|
||||
await filesApi.uploadTemplateFinished(
|
||||
{
|
||||
name: uploadFile.name,
|
||||
url,
|
||||
},
|
||||
{
|
||||
headers: { authorization: `Bearer ${accessToken}` },
|
||||
}
|
||||
);
|
||||
thunkApi.dispatch(
|
||||
openSnackbar({
|
||||
level: "info",
|
||||
message: getTranslationID("common.message.success"),
|
||||
})
|
||||
);
|
||||
return {};
|
||||
} catch (e) {
|
||||
// e ⇒ errorObjectに変換"
|
||||
const error = createErrorObject(e);
|
||||
thunkApi.dispatch(
|
||||
openSnackbar({
|
||||
level: "error",
|
||||
message: getTranslationID("common.message.internalServerError"),
|
||||
})
|
||||
);
|
||||
return thunkApi.rejectWithValue({ error });
|
||||
}
|
||||
});
|
||||
|
||||
@ -7,6 +7,9 @@ export const selectTemplates = (state: RootState) =>
|
||||
export const selectIsLoading = (state: RootState) =>
|
||||
state.template.apps.isLoading;
|
||||
|
||||
export const selectIsUploading = (state: RootState) =>
|
||||
state.template.apps.isUploading;
|
||||
|
||||
export const selectUploadFile = (state: RootState) =>
|
||||
state.template.apps.uploadFile;
|
||||
|
||||
|
||||
@ -7,6 +7,7 @@ export interface TemplateState {
|
||||
|
||||
export interface Apps {
|
||||
isLoading: boolean;
|
||||
isUploading: boolean;
|
||||
uploadFile?: File;
|
||||
}
|
||||
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import { PayloadAction, createSlice } from "@reduxjs/toolkit";
|
||||
import { TemplateState } from "./state";
|
||||
import { listTemplateAsync } from "./operations";
|
||||
import { listTemplateAsync, uploadTemplateAsync } from "./operations";
|
||||
|
||||
const initialState: TemplateState = {
|
||||
apps: {
|
||||
isLoading: false,
|
||||
isUploading: false,
|
||||
uploadFile: undefined,
|
||||
},
|
||||
domain: {},
|
||||
@ -35,6 +36,15 @@ export const templateSlice = createSlice({
|
||||
builder.addCase(listTemplateAsync.rejected, (state) => {
|
||||
state.apps.isLoading = false;
|
||||
});
|
||||
builder.addCase(uploadTemplateAsync.pending, (state) => {
|
||||
state.apps.isUploading = true;
|
||||
});
|
||||
builder.addCase(uploadTemplateAsync.fulfilled, (state) => {
|
||||
state.apps.isUploading = false;
|
||||
});
|
||||
builder.addCase(uploadTemplateAsync.rejected, (state) => {
|
||||
state.apps.isUploading = false;
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@ -1,37 +1,53 @@
|
||||
import React, { useCallback } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { getTranslationID } from "translation";
|
||||
import styles from "styles/app.module.scss";
|
||||
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { AppDispatch } from "app/store";
|
||||
import {
|
||||
cleanupTemplate,
|
||||
selectUploadFile,
|
||||
changeUploadFile,
|
||||
cleanupTemplate,
|
||||
listTemplateAsync,
|
||||
selectIsUploading,
|
||||
selectUploadFile,
|
||||
selectUploadFileError,
|
||||
uploadTemplateAsync,
|
||||
} from "features/workflow/template";
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import styles from "styles/app.module.scss";
|
||||
import { getTranslationID } from "translation";
|
||||
import close from "../../assets/images/close.svg";
|
||||
import progress_activit from "../../assets/images/progress_activit.svg";
|
||||
|
||||
interface AddTemplateFilePopupProps {
|
||||
onClose: () => void;
|
||||
isOpen: boolean;
|
||||
}
|
||||
|
||||
export const AddTemplateFilePopup: React.FC<AddTemplateFilePopupProps> = (
|
||||
props: AddTemplateFilePopupProps
|
||||
) => {
|
||||
const { onClose, isOpen } = props;
|
||||
const { onClose } = props;
|
||||
const [t] = useTranslation();
|
||||
const dispatch: AppDispatch = useDispatch();
|
||||
|
||||
// 閉じるボタンを押したときの処理
|
||||
const closePopup = useCallback(() => {
|
||||
onClose();
|
||||
dispatch(cleanupTemplate());
|
||||
}, [onClose, dispatch]);
|
||||
// 保存ボタンを押したかどうか
|
||||
const [isPushUploadButton, setIsPushUploadButton] = useState<boolean>(false);
|
||||
|
||||
// アップロード対象のファイル情報
|
||||
const uploadFile = useSelector(selectUploadFile);
|
||||
// ファイルアップロード中かどうか
|
||||
const isUploading = useSelector(selectIsUploading);
|
||||
// ファイルのエラー
|
||||
const { hasErrorFileSize, hasErrorRequired } = useSelector(
|
||||
selectUploadFileError
|
||||
);
|
||||
|
||||
// ブラウザのウィンドウが閉じられようとしている場合に発火するイベントハンドラ
|
||||
const handleBeforeUnload = (e: BeforeUnloadEvent) => {
|
||||
// テンプレートファイルアップロード中に閉じられようとしている場合、ダイアログを表示させる
|
||||
if (isUploading) {
|
||||
e.preventDefault();
|
||||
// ChromeではreturnValueが必要
|
||||
e.returnValue = "";
|
||||
}
|
||||
};
|
||||
|
||||
// ファイルが選択されたときの処理
|
||||
const handleFileChange = useCallback(
|
||||
@ -43,12 +59,48 @@ export const AddTemplateFilePopup: React.FC<AddTemplateFilePopupProps> = (
|
||||
if (file) {
|
||||
dispatch(changeUploadFile({ file }));
|
||||
}
|
||||
// 同名のファイルを選択した場合、onChangeが発火しないため、valueをクリアする
|
||||
event.target.value = "";
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
// ファイルアップロード処理
|
||||
const handleUploadFile = useCallback(async () => {
|
||||
setIsPushUploadButton(true);
|
||||
// エラーチェックを実施
|
||||
if (hasErrorFileSize || hasErrorRequired) {
|
||||
return;
|
||||
}
|
||||
// ファイルアップロード処理
|
||||
const { meta } = await dispatch(uploadTemplateAsync());
|
||||
if (meta.requestStatus === "fulfilled") {
|
||||
onClose();
|
||||
dispatch(listTemplateAsync());
|
||||
}
|
||||
}, [dispatch, hasErrorFileSize, hasErrorRequired, onClose]);
|
||||
|
||||
// コンポーネントがマウントされた時にイベントハンドラを登録する
|
||||
useEffect(() => {
|
||||
window.addEventListener("beforeunload", handleBeforeUnload);
|
||||
// コンポーネントがアンマウントされるときにイベントハンドラを解除する
|
||||
return () => {
|
||||
window.removeEventListener("beforeunload", handleBeforeUnload);
|
||||
};
|
||||
});
|
||||
|
||||
useEffect(
|
||||
() => () => {
|
||||
// useEffectのreturnとしてcleanupAppsを実行することで、ポップアップのアンマウント時に初期化を行う
|
||||
dispatch(cleanupTemplate());
|
||||
setIsPushUploadButton(false);
|
||||
},
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={`${styles.modal} ${isOpen ? styles.isShow : ""}`}>
|
||||
<div className={`${styles.modal} ${styles.isShow}`}>
|
||||
<div className={styles.modalBox}>
|
||||
<p className={styles.modalTitle}>
|
||||
{t(getTranslationID("templateFilePage.label.addTemplate"))}
|
||||
@ -57,7 +109,7 @@ export const AddTemplateFilePopup: React.FC<AddTemplateFilePopupProps> = (
|
||||
src={close}
|
||||
className={styles.modalTitleIcon}
|
||||
alt="close"
|
||||
onClick={closePopup}
|
||||
onClick={onClose}
|
||||
/>
|
||||
</p>
|
||||
<form className={styles.form}>
|
||||
@ -80,6 +132,16 @@ export const AddTemplateFilePopup: React.FC<AddTemplateFilePopupProps> = (
|
||||
onChange={handleFileChange}
|
||||
/>
|
||||
</label>
|
||||
{isPushUploadButton && hasErrorRequired && (
|
||||
<span className={`${styles.formError} ${styles.alignCenter}`}>
|
||||
{t(getTranslationID("templateFilePage.label.fileEmptyError"))}
|
||||
</span>
|
||||
)}
|
||||
{isPushUploadButton && hasErrorFileSize && (
|
||||
<span className={`${styles.formError} ${styles.alignCenter}`}>
|
||||
{t(getTranslationID("templateFilePage.label.fileSizeError"))}
|
||||
</span>
|
||||
)}
|
||||
</dd>
|
||||
<dd className={`${styles.full} ${styles.alignCenter}`}>
|
||||
<input
|
||||
@ -88,8 +150,18 @@ export const AddTemplateFilePopup: React.FC<AddTemplateFilePopupProps> = (
|
||||
value={t(
|
||||
getTranslationID("templateFilePage.label.addTemplate")
|
||||
)}
|
||||
className={`${styles.formSubmit} ${styles.marginBtm1} ${styles.isActive}`}
|
||||
onClick={handleUploadFile}
|
||||
className={`${styles.formSubmit} ${styles.marginBtm1} ${
|
||||
!isUploading ? styles.isActive : ""
|
||||
}`}
|
||||
/>
|
||||
{isUploading && (
|
||||
<img
|
||||
src={progress_activit}
|
||||
className={styles.icLoading}
|
||||
alt="Loading"
|
||||
/>
|
||||
)}
|
||||
</dd>
|
||||
</dl>
|
||||
</form>
|
||||
|
||||
@ -31,12 +31,13 @@ export const TemplateFilePage: React.FC = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
{isShowAddPopup && (
|
||||
<AddTemplateFilePopup
|
||||
onClose={() => {
|
||||
setIsShowAddPopup(false);
|
||||
}}
|
||||
isOpen={isShowAddPopup}
|
||||
/>
|
||||
)}
|
||||
<div className={styles.wrap}>
|
||||
<Header userName="XXXXXXXX" />
|
||||
<UpdateTokenTimer />
|
||||
|
||||
@ -421,7 +421,7 @@
|
||||
"chooseFile": "(de)Choose File",
|
||||
"notFileChosen": "(de)- Not file chosen -",
|
||||
"fileSizeTerms": "(de)Flie Name",
|
||||
"fileSizeError": "(de)選択されたファイルのサイズが大きすぎます。サイズが?MB以下のファイルを選択してください。",
|
||||
"fileSizeError": "(de)選択されたファイルのサイズが大きすぎます。サイズが5MB以下のファイルを選択してください。",
|
||||
"fileEmptyError": "(de)ファイル選択は必須です。ファイルを選択してください。"
|
||||
}
|
||||
},
|
||||
|
||||
@ -421,7 +421,7 @@
|
||||
"chooseFile": "Choose File",
|
||||
"notFileChosen": "- Not file chosen -",
|
||||
"fileSizeTerms": "Flie Name",
|
||||
"fileSizeError": "選択されたファイルのサイズが大きすぎます。サイズが?MB以下のファイルを選択してください。",
|
||||
"fileSizeError": "選択されたファイルのサイズが大きすぎます。サイズが5MB以下のファイルを選択してください。",
|
||||
"fileEmptyError": "ファイル選択は必須です。ファイルを選択してください。"
|
||||
}
|
||||
},
|
||||
|
||||
@ -421,7 +421,7 @@
|
||||
"chooseFile": "(es)Choose File",
|
||||
"notFileChosen": "(es)- Not file chosen -",
|
||||
"fileSizeTerms": "(es)Flie Name",
|
||||
"fileSizeError": "(es)選択されたファイルのサイズが大きすぎます。サイズが?MB以下のファイルを選択してください。",
|
||||
"fileSizeError": "(es)選択されたファイルのサイズが大きすぎます。サイズが5MB以下のファイルを選択してください。",
|
||||
"fileEmptyError": "(es)ファイル選択は必須です。ファイルを選択してください。"
|
||||
}
|
||||
},
|
||||
|
||||
@ -421,7 +421,7 @@
|
||||
"chooseFile": "(fr)Choose File",
|
||||
"notFileChosen": "(fr)- Not file chosen -",
|
||||
"fileSizeTerms": "(fr)Flie Name",
|
||||
"fileSizeError": "(fr)選択されたファイルのサイズが大きすぎます。サイズが?MB以下のファイルを選択してください。",
|
||||
"fileSizeError": "(fr)選択されたファイルのサイズが大きすぎます。サイズが5MB以下のファイルを選択してください。",
|
||||
"fileEmptyError": "(fr)ファイル選択は必須です。ファイルを選択してください。"
|
||||
}
|
||||
},
|
||||
|
||||
@ -16,4 +16,9 @@ export default defineConfig({
|
||||
minify: false,
|
||||
},
|
||||
plugins: [env(), tsconfigPaths(), react(), sassDts()],
|
||||
resolve: {
|
||||
alias: {
|
||||
os: "rollup-plugin-node-polyfills/polyfills/os",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@ -8,6 +8,7 @@ AZURE_CLIENT_SECRET=xxxxxxxx
|
||||
ADB2C_TENANT_ID=xxxxxxxx
|
||||
ADB2C_CLIENT_ID=xxxxxxxx
|
||||
ADB2C_CLIENT_SECRET=xxxxxxxx
|
||||
ADB2C_ORIGIN=https://zzzzzzzzzz
|
||||
KEY_VAULT_NAME=kv-odms-secret-dev
|
||||
JWT_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEA5IZZNgDew9eGmuFTezwdHYLSaJvUPPIKYoiOeVLD1paWNI51\n7Vkaoh0ngprcKOdv6T1N07V4igK7mOim2zY3yCTR6wcWR3PfFJrl9vh5SOo79koZ\noJb27YiM4jtxfx2dezzp0T2GoNR5rRolPUbWFJXnDe0DVXYXpJLb4LAlF2XAyYX0\nSYKUVUsJnzm5k4xbXtnwPwVbpm0EdswBE6qSfiL9zWk9dvHoKzSnfSDzDFoFcEoV\nchawzYXf/MM1YR4wo5XyzECc6Q5Ah4z522//mBNNaDHv83Yuw3mGShT73iJ0JQdk\nTturshv2Ecma38r6ftrIwNYXw4VVatJM8+GOOQIDAQABAoIBADrwp7u097+dK/tw\nWD61n3DIGAqg/lmFt8X4IH8MKLSE/FKr16CS1bqwOEuIM3ZdUtDeXd9Xs7IsyEPE\n5ZwuXK7DSF0M4+Mj8Ip49Q0Aww9aUoLQU9HGfgN/r4599GTrt31clZXA/6Mlighq\ncOZgCcEfdItz8OMu5SQuOIW4CKkCuaWnPOP26UqZocaXNZfpZH0iFLATMMH/TT8x\nay9ToHTQYE17ijdQ/EOLSwoeDV1CU1CIE3P4YfLJjvpKptly5dTevriHEzBi70Jx\n/KEPUn9Jj2gZafrUxRVhmMbm1zkeYxL3gsqRuTzRjEeeILuZhSJyCkQZyUNARxsg\nQY4DZfECgYEA+YLKUtmYTx60FS6DJ4s31TAsXY8kwhq/lB9E3GBZKDd0DPayXEeK\n4UWRQDTT6MI6fedW69FOZJ5sFLp8HQpcssb4Weq9PCpDhNTx8MCbdH3Um5QR3vfW\naKq/1XM8MDUnx5XcNYd87Aw3azvJAvOPr69as8IPnj6sKaRR9uQjbYUCgYEA6nfV\n5j0qmn0EJXZJblk4mvvjLLoWSs17j9YlrZJlJxXMDFRYtgnelv73xMxOMvcGoxn5\nifs7dpaM2x5EmA6jVU5sYaB/beZGEPWqPYGyjIwXPvUGAAv8Gbnvpp+xlSco/Dum\nIq0w+43ry5/xWh6CjfrvKV0J2bDOiJwPEdu/8iUCgYEAnBBSvL+dpN9vhFAzeOh7\nY71eAqcmNsLEUcG9MJqTKbSFwhYMOewF0iHRWHeylEPokhfBJn8kqYrtz4lVWFTC\n5o/Nh3BsLNXCpbMMIapXkeWiti1HgE9ErPMgSkJpwz18RDpYIqM8X+jEQS6D7HSr\nyxfDg+w+GJza0rEVE3hfMIECgYBw+KZ2VfhmEWBjEHhXE+QjQMR3s320MwebCUqE\nNCpKx8TWF/naVC0MwfLtvqbbBY0MHyLN6d//xpA9r3rLbRojqzKrY2KiuDYAS+3n\nzssRzxoQOozWju+8EYu30/ADdqfXyIHG6X3VZs87AGiQzGyJLmP3oR1y5y7MQa09\nJI16hQKBgHK5uwJhGa281Oo5/FwQ3uYLymbNwSGrsOJXiEu2XwJEXwVi2ELOKh4/\n03pBk3Kva3fIwEK+vCzDNnxShIQqBE76/2I1K1whOfoUehhYvKHGaXl2j70Zz9Ks\nrkGW1cx7p+yDqATDrwHBHTHFh5bUTTn8dN40n0e0W/llurpbBkJM\n-----END RSA PRIVATE KEY-----\n"
|
||||
JWT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5IZZNgDew9eGmuFTezwd\nHYLSaJvUPPIKYoiOeVLD1paWNI517Vkaoh0ngprcKOdv6T1N07V4igK7mOim2zY3\nyCTR6wcWR3PfFJrl9vh5SOo79koZoJb27YiM4jtxfx2dezzp0T2GoNR5rRolPUbW\nFJXnDe0DVXYXpJLb4LAlF2XAyYX0SYKUVUsJnzm5k4xbXtnwPwVbpm0EdswBE6qS\nfiL9zWk9dvHoKzSnfSDzDFoFcEoVchawzYXf/MM1YR4wo5XyzECc6Q5Ah4z522//\nmBNNaDHv83Yuw3mGShT73iJ0JQdkTturshv2Ecma38r6ftrIwNYXw4VVatJM8+GO\nOQIDAQAB\n-----END PUBLIC KEY-----\n"
|
||||
|
||||
@ -620,7 +620,7 @@ export class FilesService {
|
||||
await this.templateFilesRepository.upsertTemplateFile(
|
||||
accountId,
|
||||
fileName,
|
||||
url,
|
||||
fileUrl,
|
||||
);
|
||||
} catch (e) {
|
||||
this.logger.error(`error=${e}`);
|
||||
|
||||
@ -281,8 +281,8 @@ export class BlobstorageService {
|
||||
},
|
||||
sharedKeyCredential,
|
||||
);
|
||||
|
||||
const url = new URL('Templates', containerClient.url);
|
||||
// baseパスの末尾に/をつけないとパスの最後のセグメントが無視される
|
||||
const url = new URL('Templates', `${containerClient.url}/`);
|
||||
url.search = `${sasToken}`;
|
||||
|
||||
return url.toString();
|
||||
|
||||
@ -5,11 +5,17 @@ import { AppModule } from './app.module';
|
||||
import { ValidationPipe } from '@nestjs/common';
|
||||
import helmet from 'helmet';
|
||||
const helmetDirectives = helmet.contentSecurityPolicy.getDefaultDirectives();
|
||||
helmetDirectives['connect-src'] = [
|
||||
helmetDirectives['connect-src'] =
|
||||
process.env.STAGE === 'local'
|
||||
? [
|
||||
"'self'",
|
||||
'https://adb2codmsdev.b2clogin.com/adb2codmsdev.onmicrosoft.com/b2c_1_signin_dev/v2.0/.well-known/openid-configuration',
|
||||
'https://adb2codmsdev.b2clogin.com/adb2codmsdev.onmicrosoft.com/b2c_1_signin_dev/oauth2/v2.0/token',
|
||||
];
|
||||
process.env.ADB2C_ORIGIN,
|
||||
process.env.STORAGE_ACCOUNT_ENDPOINT_US,
|
||||
process.env.STORAGE_ACCOUNT_ENDPOINT_AU,
|
||||
process.env.STORAGE_ACCOUNT_ENDPOINT_EU,
|
||||
]
|
||||
: ["'self'"];
|
||||
|
||||
helmetDirectives['navigate-to'] = ["'self'"];
|
||||
helmetDirectives['style-src'] = ["'self'", 'https:'];
|
||||
helmetDirectives['report-uri'] = ["'self'"];
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user