Merge branch 'develop'

This commit is contained in:
x.yumoto.k 2023-11-07 08:16:39 +09:00
commit bea541ed24
83 changed files with 15643 additions and 12862 deletions

View File

@ -2,20 +2,11 @@
# FROM mcr.microsoft.com/azure-functions/node:4-node18-appservice
FROM mcr.microsoft.com/azure-functions/node:4-node18
RUN /bin/cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && \
echo "Asia/Tokyo" > /etc/timezone
ENV AzureWebJobsScriptRoot=/home/site/wwwroot \
AzureFunctionsJobHost__Logging__Console__IsEnabled=true
RUN apt-get -y update
RUN apt-get install -y \
curl \
gnupg
RUN apt-get install -y nodejs
WORKDIR /home/site/wwwroot
RUN cd /home/site/wwwroot
COPY ./ /home/site/wwwroot
COPY . /home/site/wwwroot
RUN npm install -g npm
RUN cd /home/site/wwwroot && \
npm install && \
npm run build

View File

@ -42,7 +42,6 @@ jobs:
resourceGroupName: 'odms-prod-rg'
slotName: 'staging'
containers: 'crodmsregistrymaintenance.azurecr.io/odmscloud/staging/dictation:$(Build.SourceVersion)'
# TODO: stagingパイプライン実装時、staging用のイメージに変更する
- job: frontend_deploy
dependsOn: backend_deploy
condition: succeeded('backend_deploy')
@ -134,12 +133,12 @@ jobs:
inputs:
script: >2
# DB接続情報書き換え
sed -i -e "s/DB_NAME/$(db-name)/g" ./dictation_server/db/dbconfig.yml
sed -i -e "s/DB_PASS/$(db-pass)/g" ./dictation_server/db/dbconfig.yml
sed -i -e "s/DB_USERNAME/$(db-user)/g" ./dictation_server/db/dbconfig.yml
sed -i -e "s/DB_PORT/$(db-port)/g" ./dictation_server/db/dbconfig.yml
sed -i -e "s/DB_HOST/$(db-host)/g" ./dictation_server/db/dbconfig.yml
sql-migrate --version
cat ./dictation_server/db/dbconfig.yml
# migration実行
sql-migrate up -config=./dictation_server/db/dbconfig.yml -env=ci
sed -i -e "s/DB_NAME/$(db-name)/g" ./dictation_server/db/dbconfig.yml
sed -i -e "s/DB_PASS/$(admin-db-pass)/g" ./dictation_server/db/dbconfig.yml
sed -i -e "s/DB_USERNAME/$(admin-db-user)/g" ./dictation_server/db/dbconfig.yml
sed -i -e "s/DB_PORT/$(db-port)/g" ./dictation_server/db/dbconfig.yml
sed -i -e "s/DB_HOST/$(db-host)/g" ./dictation_server/db/dbconfig.yml
sql-migrate --version
cat ./dictation_server/db/dbconfig.yml
# migration実行
sql-migrate up -config=./dictation_server/db/dbconfig.yml -env=ci

View File

@ -198,29 +198,93 @@ jobs:
condition: succeeded('frontend_build_production')
displayName: Backend Deploy
pool:
name: odms-deploy-pipeline
vmImage: ubuntu-latest
steps:
- checkout: self
clean: true
fetchDepth: 1
# TODO: Productionと同様にデプロイを行う
- task: AzureWebAppContainer@1
inputs:
azureSubscription: 'omds-service-connection-stg'
appName: 'app-odms-dictation-stg'
deployToSlotOrASE: true
resourceGroupName: 'stg-application-rg'
slotName: 'staging'
containers: 'crodmsregistrymaintenance.azurecr.io/odmscloud/staging/dictation:$(Build.SourceVersion)'
- job: frontend_deploy
dependsOn: backend_deploy
condition: succeeded('backend_deploy')
displayName: Deploy Frontend Files
variables:
storageAccountName: saomdspipeline
containerName: staging
environment: staging
pool:
vmImage: ubuntu-latest
steps:
- checkout: self
clean: true
fetchDepth: 1
- task: AzureCLI@2
inputs:
azureSubscription: 'omds-service-connection-stg'
scriptType: 'bash'
scriptLocation: 'inlineScript'
inlineScript: |
az storage blob download \
--auth-mode login \
--account-name $(storageAccountName) \
--container-name $(environment) \
--name $(Build.SourceVersion).zip \
--file $(Build.SourcesDirectory)/$(Build.SourceVersion).zip
- task: Bash@3
displayName: Bash Script
inputs:
targetType: inline
script: unzip $(Build.SourcesDirectory)/$(Build.SourceVersion).zip -d $(Build.SourcesDirectory)/$(Build.SourceVersion)
- task: AzureStaticWebApp@0
displayName: 'Static Web App: '
inputs:
workingDirectory: '$(Build.SourcesDirectory)'
app_location: '/$(Build.SourceVersion)'
config_file_location: /dictation_client
skip_app_build: true
skip_api_build: true
is_static_export: false
verbose: false
azure_static_web_apps_api_token: $(STATIC_DICTATION_DEPLOYMENT_TOKEN)
- job: smoke_test
dependsOn: frontend_deploy
condition: succeeded('frontend_deploy')
displayName: 'smoke test'
pool:
name: odms-deploy-pipeline
steps:
- checkout: self
clean: true
fetchDepth: 1
# TODO: Productionと同様にデプロイを行う
# スモークテスト用にjobを確保
- job: swap_slot
dependsOn: smoke_test
condition: succeeded('smoke_test')
displayName: 'Swap Staging and Production'
pool:
name: odms-deploy-pipeline
steps:
- checkout: self
clean: true
fetchDepth: 1
- task: AzureAppServiceManage@0
displayName: 'Azure App Service Manage: app-odms-dictation-stg'
inputs:
azureSubscription: 'omds-service-connection-stg'
action: 'Swap Slots'
WebAppName: 'app-odms-dictation-stg'
ResourceGroupName: 'stg-application-rg'
SourceSlot: 'staging'
SwapWithProduction: true
- job: migration
dependsOn: frontend_deploy
condition: succeeded('frontend_deploy')
dependsOn: swap_slot
condition: succeeded('swap_slot')
displayName: DB migration
pool:
name: odms-deploy-pipeline
@ -228,4 +292,22 @@ jobs:
- checkout: self
clean: true
fetchDepth: 1
# TODO: Productionと同様にマイグレーションを行う
- task: AzureKeyVault@2
displayName: 'Azure Key Vault: kv-odms-secret-stg'
inputs:
ConnectedServiceName: 'omds-service-connection-stg'
KeyVaultName: kv-odms-secret-stg
- task: CmdLine@2
displayName: migration
inputs:
script: >2
# DB接続情報書き換え
sed -i -e "s/DB_NAME/$(db-name)/g" ./dictation_server/db/dbconfig.yml
sed -i -e "s/DB_PASS/$(admin-db-pass)/g" ./dictation_server/db/dbconfig.yml
sed -i -e "s/DB_USERNAME/$(admin-db-user)/g" ./dictation_server/db/dbconfig.yml
sed -i -e "s/DB_PORT/$(db-port)/g" ./dictation_server/db/dbconfig.yml
sed -i -e "s/DB_HOST/$(db-host)/g" ./dictation_server/db/dbconfig.yml
sql-migrate --version
cat ./dictation_server/db/dbconfig.yml
# migration実行
sql-migrate up -config=./dictation_server/db/dbconfig.yml -env=ci

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -8,17 +8,14 @@
},
"resources": [
{
"apiVersion": "2017-04-01",
"apiVersion": "2023-09-01",
"location": "Japan East",
"name": "[parameters('namespaces_ntfns_odms_dev_name')]",
"properties": {
"createdAt": "2023-07-24T01:26:14.6870000Z",
"critical": false,
"enabled": true,
"namespaceType": "NotificationHub",
"provisioningState": "Succeeded",
"serviceBusEndpoint": "[concat('https://', parameters('namespaces_ntfns_odms_dev_name'), '.servicebus.windows.net:443/')]",
"status": "Active",
"updatedAt": "2023-07-24T01:26:14.7370000Z"
"publicNetworkAccess": "Enabled",
"status": "Created"
},
"sku": {
"name": "Free"
@ -26,11 +23,10 @@
"type": "Microsoft.NotificationHubs/namespaces"
},
{
"apiVersion": "2017-04-01",
"apiVersion": "2023-09-01",
"dependsOn": [
"[resourceId('Microsoft.NotificationHubs/namespaces', parameters('namespaces_ntfns_odms_dev_name'))]"
],
"location": "Japan East",
"name": "[concat(parameters('namespaces_ntfns_odms_dev_name'), '/RootManageSharedAccessKey')]",
"properties": {
"rights": [
@ -39,32 +35,31 @@
"Send"
]
},
"type": "Microsoft.NotificationHubs/namespaces/AuthorizationRules"
"type": "Microsoft.NotificationHubs/namespaces/authorizationRules"
},
{
"apiVersion": "2017-04-01",
"apiVersion": "2023-09-01",
"dependsOn": [
"[resourceId('Microsoft.NotificationHubs/namespaces', parameters('namespaces_ntfns_odms_dev_name'))]"
],
"location": "Japan East",
"name": "[concat(parameters('namespaces_ntfns_odms_dev_name'), '/ntf-odms-dev')]",
"properties": {
"authorizationRules": [],
"name": "ntf-odms-dev",
"registrationTtl": "10675199.02:48:05.4775807"
},
"tags": {
"Project": "OMDS",
"environment": "develop"
},
"type": "Microsoft.NotificationHubs/namespaces/NotificationHubs"
"type": "Microsoft.NotificationHubs/namespaces/notificationHubs"
},
{
"apiVersion": "2017-04-01",
"apiVersion": "2023-09-01",
"dependsOn": [
"[resourceId('Microsoft.NotificationHubs/namespaces/NotificationHubs', parameters('namespaces_ntfns_odms_dev_name'), 'ntf-odms-dev')]",
"[resourceId('Microsoft.NotificationHubs/namespaces/notificationHubs', parameters('namespaces_ntfns_odms_dev_name'), 'ntf-odms-dev')]",
"[resourceId('Microsoft.NotificationHubs/namespaces', parameters('namespaces_ntfns_odms_dev_name'))]"
],
"location": "Japan East",
"name": "[concat(parameters('namespaces_ntfns_odms_dev_name'), '/ntf-odms-dev/DefaultFullSharedAccessSignature')]",
"properties": {
"rights": [
@ -73,22 +68,21 @@
"Send"
]
},
"type": "Microsoft.NotificationHubs/namespaces/NotificationHubs/AuthorizationRules"
"type": "Microsoft.NotificationHubs/namespaces/notificationHubs/authorizationRules"
},
{
"apiVersion": "2017-04-01",
"apiVersion": "2023-09-01",
"dependsOn": [
"[resourceId('Microsoft.NotificationHubs/namespaces/NotificationHubs', parameters('namespaces_ntfns_odms_dev_name'), 'ntf-odms-dev')]",
"[resourceId('Microsoft.NotificationHubs/namespaces/notificationHubs', parameters('namespaces_ntfns_odms_dev_name'), 'ntf-odms-dev')]",
"[resourceId('Microsoft.NotificationHubs/namespaces', parameters('namespaces_ntfns_odms_dev_name'))]"
],
"location": "Japan East",
"name": "[concat(parameters('namespaces_ntfns_odms_dev_name'), '/ntf-odms-dev/DefaultListenSharedAccessSignature')]",
"properties": {
"rights": [
"Listen"
]
},
"type": "Microsoft.NotificationHubs/namespaces/NotificationHubs/AuthorizationRules"
"type": "Microsoft.NotificationHubs/namespaces/notificationHubs/authorizationRules"
}
],
"variables": {}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -8,17 +8,14 @@
},
"resources": [
{
"apiVersion": "2017-04-01",
"apiVersion": "2023-09-01",
"location": "Japan East",
"name": "[parameters('namespaces_ntfns_odms_stg_name')]",
"properties": {
"createdAt": "2023-07-25T01:57:35.5400000Z",
"critical": false,
"enabled": true,
"namespaceType": "NotificationHub",
"provisioningState": "Succeeded",
"serviceBusEndpoint": "[concat('https://', parameters('namespaces_ntfns_odms_stg_name'), '.servicebus.windows.net:443/')]",
"status": "Active",
"updatedAt": "2023-07-25T01:57:35.5970000Z"
"publicNetworkAccess": "Enabled",
"status": "Created"
},
"sku": {
"name": "Free"
@ -26,11 +23,10 @@
"type": "Microsoft.NotificationHubs/namespaces"
},
{
"apiVersion": "2017-04-01",
"apiVersion": "2023-09-01",
"dependsOn": [
"[resourceId('Microsoft.NotificationHubs/namespaces', parameters('namespaces_ntfns_odms_stg_name'))]"
],
"location": "Japan East",
"name": "[concat(parameters('namespaces_ntfns_odms_stg_name'), '/RootManageSharedAccessKey')]",
"properties": {
"rights": [
@ -39,32 +35,31 @@
"Send"
]
},
"type": "Microsoft.NotificationHubs/namespaces/AuthorizationRules"
"type": "Microsoft.NotificationHubs/namespaces/authorizationRules"
},
{
"apiVersion": "2017-04-01",
"apiVersion": "2023-09-01",
"dependsOn": [
"[resourceId('Microsoft.NotificationHubs/namespaces', parameters('namespaces_ntfns_odms_stg_name'))]"
],
"location": "Japan East",
"name": "[concat(parameters('namespaces_ntfns_odms_stg_name'), '/ntf-odms-stg')]",
"properties": {
"authorizationRules": [],
"name": "ntf-odms-stg",
"registrationTtl": "10675199.02:48:05.4775807"
},
"tags": {
"Project": "OMDS",
"environment": "staging"
},
"type": "Microsoft.NotificationHubs/namespaces/NotificationHubs"
"type": "Microsoft.NotificationHubs/namespaces/notificationHubs"
},
{
"apiVersion": "2017-04-01",
"apiVersion": "2023-09-01",
"dependsOn": [
"[resourceId('Microsoft.NotificationHubs/namespaces/NotificationHubs', parameters('namespaces_ntfns_odms_stg_name'), 'ntf-odms-stg')]",
"[resourceId('Microsoft.NotificationHubs/namespaces/notificationHubs', parameters('namespaces_ntfns_odms_stg_name'), 'ntf-odms-stg')]",
"[resourceId('Microsoft.NotificationHubs/namespaces', parameters('namespaces_ntfns_odms_stg_name'))]"
],
"location": "Japan East",
"name": "[concat(parameters('namespaces_ntfns_odms_stg_name'), '/ntf-odms-stg/DefaultFullSharedAccessSignature')]",
"properties": {
"rights": [
@ -73,22 +68,21 @@
"Send"
]
},
"type": "Microsoft.NotificationHubs/namespaces/NotificationHubs/AuthorizationRules"
"type": "Microsoft.NotificationHubs/namespaces/notificationHubs/authorizationRules"
},
{
"apiVersion": "2017-04-01",
"apiVersion": "2023-09-01",
"dependsOn": [
"[resourceId('Microsoft.NotificationHubs/namespaces/NotificationHubs', parameters('namespaces_ntfns_odms_stg_name'), 'ntf-odms-stg')]",
"[resourceId('Microsoft.NotificationHubs/namespaces/notificationHubs', parameters('namespaces_ntfns_odms_stg_name'), 'ntf-odms-stg')]",
"[resourceId('Microsoft.NotificationHubs/namespaces', parameters('namespaces_ntfns_odms_stg_name'))]"
],
"location": "Japan East",
"name": "[concat(parameters('namespaces_ntfns_odms_stg_name'), '/ntf-odms-stg/DefaultListenSharedAccessSignature')]",
"properties": {
"rights": [
"Listen"
]
},
"type": "Microsoft.NotificationHubs/namespaces/NotificationHubs/AuthorizationRules"
"type": "Microsoft.NotificationHubs/namespaces/notificationHubs/authorizationRules"
}
],
"variables": {}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -38,6 +38,7 @@ export const errorCodes = [
"E010401", // PONumber重複エラー
"E010501", // アカウント不在エラー
"E010502", // アカウント情報変更不可エラー
"E010503", // 代行操作不許可エラー
"E010601", // タスク変更不可エラー(タスクが変更できる状態でない、またはタスクが存在しない)
"E010602", // タスク変更権限不足エラー
"E010603", // タスク不在エラー

View File

@ -1,11 +1,11 @@
import { useEffect } from "react";
import {
isAuthenticatedSelector,
isTokenExpired,
} from "features/auth/selectors";
import { useDispatch, useSelector } from "react-redux";
import { AppDispatch } from "app/store";
import { clearToken } from "features/auth";
import {
clearToken,
isAuthenticatedSelector,
isTokenExpired,
} from "features/auth";
import { useMsal } from "@azure/msal-react";
import { clearUserInfo } from "features/login";

View File

@ -2,8 +2,7 @@ import React, { useCallback } from "react";
import { AppDispatch } from "app/store";
import { decodeToken } from "common/decodeToken";
import { useInterval } from "common/useInterval";
import { updateTokenAsync } from "features/auth/operations";
import { loadAccessToken } from "features/auth/utils";
import { updateTokenAsync, loadAccessToken } from "features/auth";
import { DateTime } from "luxon";
import { useDispatch } from "react-redux";
// アクセストークンを更新する基準の秒数

View File

@ -1,22 +1,30 @@
// 代行操作中に表示するコンポーネント
// ------------------------------------------------------
import React from "react";
import { useSelector } from "react-redux";
import React, { useCallback } from "react";
import { useTranslation } from "react-i18next";
import { getTranslationID } from "translation";
import { useDispatch, useSelector } from "react-redux";
import {
cleanupDelegateAccount,
selectDelegatedCompanyName,
} from "features/partner";
import { clearDelegationToken } from "features/auth";
import { useNavigate } from "react-router-dom";
import styles from "../../styles/app.module.scss";
import exit from "../../assets/images/exit.svg";
import reportWhite from "../../assets/images/report_white.svg";
import { getTranslationID } from "translation";
interface DelegationBarProps {
delegatedCompanyName: string;
}
export const DelegationBar: React.FC<DelegationBarProps> = (
porps
): JSX.Element => {
const { delegatedCompanyName } = porps;
export const DelegationBar: React.FC = (): JSX.Element => {
const delegatedCompanyName = useSelector(selectDelegatedCompanyName);
const { t } = useTranslation();
const dispatch = useDispatch();
const navigate = useNavigate();
const onClickExit = useCallback(() => {
dispatch(clearDelegationToken());
dispatch(cleanupDelegateAccount());
navigate("/partners");
}, [dispatch, navigate]);
return (
<div className={styles.manageInfo}>
@ -25,11 +33,13 @@ export const DelegationBar: React.FC<DelegationBarProps> = (
{t(getTranslationID("common.label.operationInsteadOf"))}
<span>{delegatedCompanyName}</span>
</p>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-noninteractive-element-interactions */}
<img
src={exit}
className={styles.manageIconClose}
alt="Exit"
title="Exit"
onClick={onClickExit}
/>
</div>
);

View File

@ -1,3 +1,4 @@
import { getTranslationID } from "translation";
import { HeaderMenus, LoginedPaths } from "./types";
export const HEADER_MENUS_ACCOUNT = "Account";
@ -8,17 +9,45 @@ export const HEADER_MENUS_WORKFLOW = "Workflow";
export const HEADER_MENUS_PARTNER = "Partners";
export const HEADER_MENUS_XXX = "XXX"; // XXX 仮のタブ
export const HEADER_MENUS: { label: HeaderMenus; path: LoginedPaths }[] = [
{ label: HEADER_MENUS_ACCOUNT, path: "/account" },
{ label: HEADER_MENUS_DICTATIONS, path: "/dictations" },
{ label: HEADER_MENUS_LICENSE, path: "/license" },
{ label: HEADER_MENUS_USER, path: "/user" },
{ label: HEADER_MENUS_WORKFLOW, path: "/workflow" },
{ label: HEADER_MENUS_PARTNER, path: "/partners" },
{ label: HEADER_MENUS_XXX, path: "/xxx" }, // XXX 仮のタブ
export const HEADER_MENUS: {
key: HeaderMenus;
label: string;
path: LoginedPaths;
}[] = [
{
key: HEADER_MENUS_ACCOUNT,
label: getTranslationID("common.label.headerAccount"),
path: "/account",
},
{
key: HEADER_MENUS_DICTATIONS,
label: getTranslationID("common.label.headerDictations"),
path: "/dictations",
},
{
key: HEADER_MENUS_LICENSE,
label: getTranslationID("common.label.headerLicense"),
path: "/license",
},
{
key: HEADER_MENUS_USER,
label: getTranslationID("common.label.headerUser"),
path: "/user",
},
{
key: HEADER_MENUS_WORKFLOW,
label: getTranslationID("common.label.headerWorkflow"),
path: "/workflow",
},
{
key: HEADER_MENUS_PARTNER,
label: getTranslationID("common.label.headerPartners"),
path: "/partners",
},
{ key: HEADER_MENUS_XXX, label: "xxx", path: "/xxx" }, // XXX 仮のタブ
];
export const HEADER_NAME = "ODMS Cloud";
export const HEADER_NAME = getTranslationID("common.label.headerName");
/**
* adminのみに表示するヘッダータブ

View File

@ -11,6 +11,7 @@ import {
selectIsUserNameEmpty,
clearUserInfo,
} from "features/login";
import { useNavigate } from "react-router-dom";
import { getFilteredMenus } from "./utils";
import logo from "../../assets/images/OMS_logo_black.svg";
import ac from "../../assets/images/account_circle.svg";
@ -30,6 +31,7 @@ const LoginedHeader: React.FC<HeaderProps> = (props: HeaderProps) => {
const dispatch: AppDispatch = useDispatch();
const { instance } = useMsal();
const { t } = useTranslation();
const navigate = useNavigate();
// Headerのユーザー情報を取得する
const isUserNameEmpty = useSelector(selectIsUserNameEmpty);
@ -61,20 +63,21 @@ const LoginedHeader: React.FC<HeaderProps> = (props: HeaderProps) => {
<div className={styles.headerLogo}>
<img src={logo} alt="OM System" />
</div>
<div className={styles.headerSub}>{HEADER_NAME}</div>
<div className={styles.headerSub}>{t(HEADER_NAME)}</div>
<div className={styles.headerMenu}>
<ul>
{filterMenus.map((x) => (
<li key={x.label}>
<li key={x.key}>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */}
<a
href={x.path}
onClick={() => navigate(x.path)}
className={
activePath.toUpperCase() === x.path.toUpperCase()
? styles.isActive
: ""
}
>
{x.label}
{t(x.label)}
</a>
</li>
))}

View File

@ -1,8 +1,4 @@
import {
isAdminUser,
isApproveTier,
isStandardUser,
} from "features/auth/utils";
import { isAdminUser, isApproveTier, isStandardUser } from "features/auth";
import { LoginedPaths } from "./types";
import {
ADMIN_ONLY_TABS,
@ -49,21 +45,17 @@ export const getFilteredMenus = () => {
return HEADER_MENUS;
}
if (isStandard) {
return HEADER_MENUS.filter(
(item) => !ADMIN_ONLY_TABS.includes(item.label)
);
return HEADER_MENUS.filter((item) => !ADMIN_ONLY_TABS.includes(item.key));
}
}
if (isTier5) {
if (isAdmin) {
return HEADER_MENUS.filter(
(item) => !TIER1_TO_TIER4_ONLY_TABS.includes(item.label)
(item) => !TIER1_TO_TIER4_ONLY_TABS.includes(item.key)
);
}
if (isStandard) {
return HEADER_MENUS.filter(
(item) => !ADMIN_ONLY_TABS.includes(item.label)
);
return HEADER_MENUS.filter((item) => !ADMIN_ONLY_TABS.includes(item.key));
}
}
// admin,standardでなく、第15階層でもないアカウントに表示する空のヘッダータブ

View File

@ -1,11 +1,10 @@
import React, { useCallback, useEffect, useRef } from "react";
import styles from "styles/app.module.scss";
import { SnackbarLevel } from "features/ui/types";
import reportWhite from "../../assets/images/report_white.svg";
import closeWhite from "../../assets/images/close_white.svg";
import checkCircleWhite from "../../assets/images/check_circle_white.svg";
export type SnackbarLevel = "info" | "error";
interface SnackbarProps {
isOpen: boolean;
level: SnackbarLevel;

View File

@ -3,6 +3,7 @@ import type { RootState } from "app/store";
import { ErrorObject, createErrorObject } from "common/errors";
import { getTranslationID } from "translation";
import { openSnackbar } from "features/ui/uiSlice";
import { getAccessToken } from "features/auth";
import {
AccountsApi,
UpdateAccountInfoRequest,
@ -26,7 +27,8 @@ export const getAccountRelationsAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);
const usersApi = new UsersApi(config);
@ -72,7 +74,8 @@ export const updateAccountInfoAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountApi = new AccountsApi(config);
@ -125,7 +128,8 @@ export const deleteAccountAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountApi = new AccountsApi(config);

View File

@ -8,14 +8,15 @@ import {
saveRefreshToken,
removeRefreshToken,
} from "./utils";
import { AuthState } from "./state";
import type { AuthState } from "./state";
import { getDelegationTokenAsync, updateTokenAsync } from "./operations";
const initialState: AuthState = {
configuration: initialConfig(),
accessToken: loadAccessToken(),
refreshToken: loadRefreshToken(),
delegatedAccessToken: null,
delegatedRefreshToken: null,
delegationAccessToken: null,
delegationRefreshToken: null,
};
export const authSlice = createSlice({
@ -24,12 +25,10 @@ export const authSlice = createSlice({
reducers: {
setToken: (
state,
action: PayloadAction<
Omit<
AuthState,
"configuration" | "delegatedAccessToken" | "delegatedRefreshToken"
>
>
action: PayloadAction<{
accessToken: string | null;
refreshToken: string | null;
}>
) => {
const { accessToken, refreshToken } = action.payload;
if (accessToken && refreshToken) {
@ -46,9 +45,25 @@ export const authSlice = createSlice({
removeAccessToken();
removeRefreshToken();
},
clearDelegationToken: (state) => {
state.delegationAccessToken = null;
state.delegationRefreshToken = null;
},
},
extraReducers: (builder) => {
builder.addCase(updateTokenAsync.fulfilled, (state, action) => {
const { accessToken } = action.payload;
state.accessToken = accessToken;
saveAccessToken(accessToken);
});
builder.addCase(getDelegationTokenAsync.fulfilled, (state, action) => {
const { accessToken, refreshToken } = action.payload;
state.delegationAccessToken = accessToken;
state.delegationRefreshToken = refreshToken;
});
},
});
export const { setToken, clearToken } = authSlice.actions;
export const { setToken, clearToken, clearDelegationToken } = authSlice.actions;
export default authSlice.reducer;

View File

@ -1 +1,5 @@
export { authSlice, clearToken, setToken } from "./authSlice";
export * from "./authSlice";
export * from "./state";
export * from "./operations";
export * from "./selectors";
export * from "./utils";

View File

@ -1,13 +1,18 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import { RootState } from "app/store";
import { AuthApi } from "../../api/api";
import type { RootState } from "app/store";
import { openSnackbar } from "features/ui";
import { getTranslationID } from "translation";
import { ErrorObject, createErrorObject } from "common/errors";
import {
AccessTokenResponse,
AuthApi,
DelegationTokenResponse,
} from "../../api/api";
import { Configuration } from "../../api/configuration";
import { setToken } from "./authSlice";
import { getAccessToken, loadRefreshToken } from "./utils";
export const updateTokenAsync = createAsyncThunk<
{
/* Empty Object */
},
AccessTokenResponse,
void,
{
// rejectした時の返却値の型
@ -19,7 +24,8 @@ export const updateTokenAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, refreshToken } = state.auth;
const { configuration } = state.auth;
const refreshToken = loadRefreshToken();
const config = new Configuration(configuration);
const authApi = new AuthApi(config);
@ -27,16 +33,70 @@ export const updateTokenAsync = createAsyncThunk<
const { data } = await authApi.accessToken({
headers: { authorization: `Bearer ${refreshToken}` },
});
// アクセストークン・リフレッシュトークンをlocalStorageに保存
thunkApi.dispatch(
setToken({
accessToken: data.accessToken,
refreshToken: state.auth.refreshToken,
})
);
return {};
return data;
} catch (e) {
return thunkApi.rejectWithValue({});
}
});
// パートナーのアカウントを代理操作するトークンを取得する
export const getDelegationTokenAsync = createAsyncThunk<
// 正常時の戻り値の型
DelegationTokenResponse,
void,
{
// rejectした時の返却値の型
rejectValue: {
error: ErrorObject;
};
}
>("auth/getDelegationTokenAsync", async (args, thunkApi) => {
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const { delegatedAccountId } = state.partner.apps;
const config = new Configuration(configuration);
const authApi = new AuthApi(config);
try {
if (!delegatedAccountId) {
throw new Error("delegatedAccountId is not set");
}
const { data } = await authApi.delegationToken(
{ delegatedAccountId },
{ headers: { authorization: `Bearer ${accessToken}` } }
);
thunkApi.dispatch(
openSnackbar({
level: "info",
message: getTranslationID("common.message.success"),
})
);
return data;
} catch (e) {
const error = createErrorObject(e);
let errorMessage = getTranslationID("common.message.internalServerError");
if (error.code === "E010503") {
errorMessage = getTranslationID(
"partnerPage.message.delegateNotAllowedError"
);
}
if (error.code === "E010501") {
errorMessage = getTranslationID("partnerPage.message.deleteFailedError");
}
thunkApi.dispatch(
openSnackbar({
level: "error",
message: errorMessage,
})
);
return thunkApi.rejectWithValue({ error });
}
});

View File

@ -1,6 +1,7 @@
import { RootState } from "app/store";
import type { RootState } from "app/store";
import { decodeToken } from "common/decodeToken";
import { DateTime } from "luxon";
import { loadAccessToken } from "./utils";
/**
*
* @param state RootState
@ -24,5 +25,9 @@ export const isTokenExpired = (state: RootState): boolean => {
};
// 代行操作用のトークンを取得する
export const selectDelegatedAccessToken = (state: RootState): string | null =>
state.auth.delegatedAccessToken;
export const selectDelegationAccessToken = (state: RootState): string | null =>
state.auth.delegationAccessToken;
// 代行操作用がなければ通常トークンを取得する
export const selectAccessToken = (state: RootState): string | null =>
state.auth.delegationAccessToken ?? loadAccessToken();

View File

@ -4,6 +4,6 @@ export interface AuthState {
configuration: ConfigurationParameters;
accessToken: string | null;
refreshToken: string | null;
delegatedAccessToken: string | null;
delegatedRefreshToken: string | null;
delegationAccessToken: string | null;
delegationRefreshToken: string | null;
}

View File

@ -1,6 +1,7 @@
import { ConfigurationParameters } from "api";
import { decodeToken } from "../../common/decodeToken";
import { ADMIN_ROLES, USER_ROLES } from "../../components/auth/constants";
import type { AuthState } from "./state";
/**
* Get access token
@ -42,6 +43,22 @@ export const removeRefreshToken = (): void => {
localStorage.removeItem("refreshToken");
};
/**
*
*
* @returns access token
*/
export const getAccessToken = (state: AuthState): string | null =>
state.delegationAccessToken ?? state.accessToken;
/**
*
*
* @returns refresh token
*/
export const getRefreshToken = (state: AuthState): string | null =>
state.delegationRefreshToken ?? state.refreshToken;
// 初期状態のAPI Config
export const initialConfig = (): ConfigurationParameters => {
const config: ConfigurationParameters = {};

View File

@ -2,6 +2,7 @@ import { createAsyncThunk } from "@reduxjs/toolkit";
import type { RootState } from "app/store";
import { getTranslationID } from "translation";
import { openSnackbar } from "features/ui/uiSlice";
import { getAccessToken } from "features/auth";
import {
TasksResponse,
TasksApi,
@ -42,7 +43,8 @@ export const listTasksAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const tasksApi = new TasksApi(config);
@ -89,7 +91,8 @@ export const getSortColumnAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const usersApi = new UsersApi(config);
@ -138,7 +141,8 @@ export const listTypistsAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);
@ -174,7 +178,8 @@ export const listTypistGroupsAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);
@ -217,7 +222,8 @@ export const updateAssigneeAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const tasksApi = new TasksApi(config);
@ -283,7 +289,8 @@ export const playbackAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const tasksApi = new TasksApi(config);
const usersApi = new UsersApi(config);

View File

@ -1,4 +1,5 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import { getAccessToken } from "features/auth";
import { openSnackbar } from "../../ui/uiSlice";
import type { RootState } from "../../../app/store";
import { getTranslationID } from "../../../translation";
@ -26,7 +27,8 @@ export const activateCardLicenseAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const licensesApi = new LicensesApi(config);

View File

@ -1,4 +1,5 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import { getAccessToken } from "features/auth";
import { openSnackbar } from "../../ui/uiSlice";
import type { RootState } from "../../../app/store";
import { getTranslationID } from "../../../translation";
@ -24,7 +25,8 @@ export const createCardLicenseAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const licensesApi = new LicensesApi(config);

View File

@ -2,6 +2,7 @@ import { createAsyncThunk } from "@reduxjs/toolkit";
import type { RootState } from "app/store";
import { getTranslationID } from "translation";
import { openSnackbar } from "features/ui/uiSlice";
import { getAccessToken } from "features/auth";
import { LicensesApi } from "../../../api/api";
import { Configuration } from "../../../api/configuration";
import { ErrorObject, createErrorObject } from "../../../common/errors";
@ -27,7 +28,8 @@ export const orderLicenseAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const licensesApi = new LicensesApi(config);

View File

@ -2,6 +2,7 @@ import { createAsyncThunk } from "@reduxjs/toolkit";
import type { RootState } from "app/store";
import { getTranslationID } from "translation";
import { openSnackbar } from "features/ui/uiSlice";
import { getAccessToken } from "features/auth";
import { AccountsApi, LicensesApi } from "../../../api/api";
import { Configuration } from "../../../api/configuration";
import { ErrorObject, createErrorObject } from "../../../common/errors";
@ -26,7 +27,8 @@ export const getLicenseOrderHistoriesAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);
@ -95,7 +97,8 @@ export const issueLicenseAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);
@ -161,7 +164,8 @@ export const cancelOrderAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const licensesApi = new LicensesApi(config);
@ -223,7 +227,8 @@ export const cancelIssueAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);

View File

@ -2,6 +2,7 @@ import { createAsyncThunk } from "@reduxjs/toolkit";
import type { RootState } from "app/store";
import { getTranslationID } from "translation";
import { openSnackbar } from "features/ui/uiSlice";
import { getAccessToken } from "features/auth";
import {
AccountsApi,
GetLicenseSummaryResponse,
@ -25,7 +26,8 @@ export const getLicenseSummaryAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);

View File

@ -1,4 +1,5 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import { getAccessToken } from "features/auth";
import type { RootState } from "../../../app/store";
import { getTranslationID } from "../../../translation";
import { openSnackbar } from "../../ui/uiSlice";
@ -25,7 +26,8 @@ export const getMyAccountAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);
try {
@ -67,7 +69,8 @@ export const getPartnerLicenseAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);

View File

@ -1,6 +1,6 @@
import { createSlice } from "@reduxjs/toolkit";
import { getUserInfoAsync } from "./operations";
import { HeaderState } from "./state";
import type { HeaderState } from "./state";
const initialState: HeaderState = {
domain: {

View File

@ -1,5 +1,5 @@
import { PayloadAction, createSlice } from "@reduxjs/toolkit";
import { LoginState } from "./state";
import type { LoginState } from "./state";
import { loginAsync } from "./operations";
const initialState: LoginState = {

View File

@ -1,14 +1,17 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import type { RootState } from "app/store";
import { setToken } from "features/auth/authSlice";
import { AuthApi, UsersApi, GetMyUserResponse } from "../../api/api";
import { setToken, getAccessToken } from "features/auth";
import {
AuthApi,
UsersApi,
GetMyUserResponse,
TokenResponse,
} from "../../api/api";
import { Configuration } from "../../api/configuration";
import { ErrorObject, createErrorObject } from "../../common/errors";
export const loginAsync = createAsyncThunk<
{
//
},
TokenResponse,
{
idToken: string;
},
@ -40,7 +43,7 @@ export const loginAsync = createAsyncThunk<
})
);
return {};
return data;
} catch (e) {
// e ⇒ errorObjectに変換"
const error = createErrorObject(e);
@ -63,7 +66,8 @@ export const getUserInfoAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const usersApi = new UsersApi(config);

View File

@ -1,4 +1,4 @@
import { RootState } from "app/store";
import type { RootState } from "app/store";
export const selectLoginApiCallStatus = (
state: RootState

View File

@ -3,6 +3,7 @@ import type { RootState } from "app/store";
import { ErrorObject, createErrorObject } from "common/errors";
import { getTranslationID } from "translation";
import { openSnackbar } from "features/ui/uiSlice";
import { getAccessToken } from "features/auth";
import {
AccountsApi,
CreatePartnerAccountRequest,
@ -26,7 +27,8 @@ export const createPartnerAccountAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountApi = new AccountsApi(config);
@ -86,7 +88,8 @@ export const getPartnerInfoAsync = createAsyncThunk<
const { limit, offset } = args;
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);

View File

@ -60,6 +60,21 @@ export const partnerSlice = createSlice({
state.apps.limit = limit;
state.apps.offset = offset;
},
changeDelegateAccount: (
state,
action: PayloadAction<{
delegatedAccountId?: number;
delegatedCompanyName?: string;
}>
) => {
const { delegatedAccountId, delegatedCompanyName } = action.payload;
state.apps.delegatedAccountId = delegatedAccountId;
state.apps.delegatedCompanyName = delegatedCompanyName;
},
cleanupDelegateAccount: (state) => {
state.apps.delegatedAccountId = undefined;
state.apps.delegatedCompanyName = undefined;
},
},
extraReducers: (builder) => {
builder.addCase(createPartnerAccountAsync.pending, (state) => {
@ -91,5 +106,7 @@ export const {
changeCountry,
cleanupAddPartner,
savePageInfo,
changeDelegateAccount,
cleanupDelegateAccount,
} = partnerSlice.actions;
export default partnerSlice.reducer;

View File

@ -53,3 +53,9 @@ export const selectCurrentPage = (state: RootState) => {
const page = floor(offset / limit) + 1;
return page;
};
// delegation
export const selectDelegatedAccountId = (state: RootState) =>
state.partner.apps.delegatedAccountId;
export const selectDelegatedCompanyName = (state: RootState) =>
state.partner.apps.delegatedCompanyName;

View File

@ -17,4 +17,6 @@ export interface Apps {
offset: number;
addPartner: CreatePartnerAccountRequest;
isLoading: boolean;
delegatedAccountId?: number;
delegatedCompanyName?: string;
}

View File

@ -5,6 +5,7 @@ import { getTranslationID } from "translation";
import { openSnackbar } from "features/ui/uiSlice";
import { getIdTokenFromLocalStorage } from "common/token";
import { TIERS } from "components/auth/constants";
import { getAccessToken } from "features/auth";
import {
UsersApi,
GetAccountInfoMinimalAccessResponse,
@ -30,7 +31,8 @@ export const getAccountInfoMinimalAccessAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountApi = new AccountsApi(config);
@ -74,7 +76,8 @@ export const getTermsInfoAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const termsApi = new TermsApi(config);
@ -120,7 +123,8 @@ export const updateAcceptedVersionAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const userApi = new UsersApi(config);

View File

@ -2,3 +2,4 @@ export * from "./constants";
export * from "./selectors";
export * from "./state";
export * from "./uiSlice";
export * from "./types";

View File

@ -1,5 +1,5 @@
import { RootState } from "app/store";
import { SnackbarLevel } from "components/snackbar";
import type { RootState } from "app/store";
import { SnackbarLevel } from "./types";
export const selectSnackber = (
state: RootState

View File

@ -1,4 +1,4 @@
import { SnackbarLevel } from "components/snackbar";
import { SnackbarLevel } from "./types";
export interface UIState {
isOpen: boolean;

View File

@ -0,0 +1 @@
export type SnackbarLevel = "info" | "error";

View File

@ -1,5 +1,5 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { SnackbarLevel } from "components/snackbar";
import { SnackbarLevel } from "./types";
import { UIState } from "./state";
import { DEFAULT_SNACKBAR_DURATION } from "./constants";

View File

@ -3,6 +3,7 @@ import type { RootState } from "app/store";
import { USER_ROLES } from "components/auth/constants";
import { openSnackbar } from "features/ui/uiSlice";
import { getTranslationID } from "translation";
import { getAccessToken } from "features/auth";
import {
GetUsersResponse,
UsersApi,
@ -27,7 +28,8 @@ export const listUsersAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const usersApi = new UsersApi(config);
@ -67,7 +69,8 @@ export const addUserAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const usersApi = new UsersApi(config);
const { addUser } = state.user.apps;
@ -134,7 +137,8 @@ export const updateUserAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const usersApi = new UsersApi(config);
const { updateUser } = state.user.apps;
@ -217,7 +221,8 @@ export const getAllocatableLicensesAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const licensesApi = new LicensesApi(config);
@ -266,7 +271,8 @@ export const allocateLicenseAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const usersApi = new UsersApi(config);
@ -335,7 +341,8 @@ export const deallocateLicenseAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const usersApi = new UsersApi(config);

View File

@ -14,6 +14,7 @@ import {
} from "api";
import type { RootState } from "app/store";
import { ErrorObject, createErrorObject } from "common/errors";
import { getAccessToken } from "features/auth";
import { openSnackbar } from "features/ui/uiSlice";
import { getTranslationID } from "translation";
@ -30,7 +31,8 @@ export const listWorkflowAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const workflowsApi = new WorkflowsApi(config);
@ -68,7 +70,8 @@ export const createWorkflowAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const workflowsApi = new WorkflowsApi(config);
const { selectedAssignees, authorId, templateId, worktypeId } =
@ -155,7 +158,8 @@ export const updateWorkflowAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const workflowsApi = new WorkflowsApi(config);
const {
@ -258,7 +262,8 @@ export const getworkflowRelationsAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);
const templatesApi = new TemplatesApi(config);
@ -326,7 +331,8 @@ export const deleteWorkflowAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const workflowsApi = new WorkflowsApi(config);

View File

@ -10,6 +10,7 @@ import { ErrorObject, createErrorObject } from "common/errors";
import { openSnackbar } from "features/ui/uiSlice";
import { getTranslationID } from "translation";
import { BlockBlobClient, ContainerClient } from "@azure/storage-blob";
import { getAccessToken } from "features/auth";
export const listTemplateAsync = createAsyncThunk<
GetTemplatesResponse,
@ -24,7 +25,8 @@ export const listTemplateAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const templateApi = new TemplatesApi(config);
@ -62,7 +64,8 @@ export const uploadTemplateAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const { uploadFile } = state.template.apps;
const config = new Configuration(configuration);
const filesApi = new FilesApi(config);

View File

@ -2,6 +2,7 @@ import { createAsyncThunk } from "@reduxjs/toolkit";
import type { RootState } from "app/store";
import { openSnackbar } from "features/ui/uiSlice";
import { getTranslationID } from "translation";
import { getAccessToken } from "features/auth";
import {
AccountsApi,
GetTypistGroupsResponse,
@ -26,7 +27,8 @@ export const listTypistGroupsAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);
@ -62,7 +64,8 @@ export const listTypistsAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);
@ -100,7 +103,8 @@ export const createTypistGroupAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);
@ -152,7 +156,8 @@ export const getTypistGroupAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);
@ -204,7 +209,8 @@ export const updateTypistGroupAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const { updateTypistGroupId, selectedTypists, groupName } =
state.typistGroup.apps;
const config = new Configuration(configuration);

View File

@ -2,6 +2,7 @@ import { createAsyncThunk } from "@reduxjs/toolkit";
import type { RootState } from "app/store";
import { openSnackbar } from "features/ui/uiSlice";
import { getTranslationID } from "translation";
import { getAccessToken } from "features/auth";
import {
AccountsApi,
GetOptionItemsResponse,
@ -23,7 +24,8 @@ export const listWorktypesAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);
@ -63,7 +65,8 @@ export const addWorktypeAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);
// stateからworktypeIdとdescriptionを取得する
@ -130,7 +133,8 @@ export const editWorktypeAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);
// stateからselectedId,worktypeId,descriptionを取得する
@ -197,7 +201,8 @@ export const getOptionItemsAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);
// stateからselectedIdを取得する
@ -249,7 +254,8 @@ export const editOptionItemsAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);
// stateからselectedId,optionItemsを取得する
@ -315,7 +321,8 @@ export const updateActiveWorktypeAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);
const { id } = args;
@ -374,7 +381,8 @@ export const deleteWorktypeAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);

View File

@ -21,7 +21,7 @@ import {
import { useTranslation } from "react-i18next";
import { getTranslationID } from "translation";
import { TIERS } from "components/auth/constants";
import { isApproveTier } from "features/auth/utils";
import { isApproveTier } from "features/auth";
import { DeleteAccountPopup } from "./deleteAccountPopup";
import progress_activit from "../../assets/images/progress_activit.svg";

View File

@ -33,7 +33,7 @@ import {
} from "features/dictation";
import { getTranslationID } from "translation";
import { Task } from "api/api";
import { isAdminUser, isAuthorUser, isTypistUser } from "features/auth/utils";
import { isAdminUser, isAuthorUser, isTypistUser } from "features/auth";
import { STATUS, LIMIT_TASK_NUM } from "../../features/dictation";
import uploaded from "../../assets/images/uploaded.svg";
import pending from "../../assets/images/pending.svg";

View File

@ -1,17 +1,17 @@
import React, { useCallback } from "react";
import { loadAccessToken } from "features/auth/utils";
import { decodeToken } from "common/decodeToken";
import { useDispatch } from "react-redux";
import { useDispatch, useSelector } from "react-redux";
import { AppDispatch } from "app/store";
import { clearToken } from "features/auth";
import { useMsal } from "@azure/msal-react";
import { Token } from "common/token";
import { clearUserInfo } from "features/login";
import { selectAccessToken } from "features/auth/selectors";
import { LicenseSummary } from "./licenseSummary";
import PartnerLicense from "./partnerLicense";
const LicensePage: React.FC = (): JSX.Element => {
const jwt = loadAccessToken();
const jwt = useSelector(selectAccessToken);
const dispatch: AppDispatch = useDispatch();
const { instance } = useMsal();

View File

@ -1,7 +1,7 @@
/* eslint-disable jsx-a11y/control-has-associated-label */
import React, { useCallback, useEffect } from "react";
import { UpdateTokenTimer } from "components/auth/updateTokenTimer";
import { isApproveTier } from "features/auth/utils";
import { isApproveTier } from "features/auth";
import { TIERS } from "components/auth/constants";
import Footer from "components/footer";
import Header from "components/header";
@ -27,7 +27,7 @@ import {
cancelIssueAsync,
} from "features/license/licenseOrderHistory";
import { selectSelectedRow } from "features/license/partnerLicense";
import { selectDelegatedAccessToken } from "features/auth/selectors";
import { selectDelegationAccessToken } from "features/auth/selectors";
import { DelegationBar } from "components/delegate";
import undo from "../../assets/images/undo.svg";
import history from "../../assets/images/history.svg";
@ -49,7 +49,7 @@ export const LicenseOrderHistory: React.FC<LicenseOrderHistoryProps> = (
const isLoading = useSelector(selectIsLoading);
const selectedRow = useSelector(selectSelectedRow);
// 代行操作用のトークンを取得する
const delegatedAccessToken = useSelector(selectDelegatedAccessToken);
const delegationAccessToken = useSelector(selectDelegationAccessToken);
// Return押下時の処理
const returnGui = useCallback(() => {
@ -159,12 +159,11 @@ export const LicenseOrderHistory: React.FC<LicenseOrderHistoryProps> = (
return (
<div
className={`${styles.wrap} ${delegatedAccessToken ? styles.manage : ""}`}
className={`${styles.wrap} ${delegationAccessToken ? styles.manage : ""}`}
>
{
// 代行操作中の場合は、代行操作バーを表示する
// TODO 代行操作中の会社名と、代行操作用のトークンを取得があれば表示するという風にする(別タスクで)
delegatedAccessToken && <DelegationBar delegatedCompanyName="XXXXXX" />
delegationAccessToken && <DelegationBar />
}
<Header />
<UpdateTokenTimer />

View File

@ -12,7 +12,7 @@ import {
selecLicenseSummaryInfo,
} from "features/license/licenseSummary";
import { selectSelectedRow } from "features/license/partnerLicense";
import { selectDelegatedAccessToken } from "features/auth/selectors";
import { selectDelegationAccessToken } from "features/auth/selectors";
import { DelegationBar } from "components/delegate";
import postAdd from "../../assets/images/post_add.svg";
import history from "../../assets/images/history.svg";
@ -36,7 +36,7 @@ export const LicenseSummary: React.FC<LicenseSummaryProps> = (
const [t] = useTranslation();
const selectedRow = useSelector(selectSelectedRow);
// 代行操作用のトークンを取得する
const delegatedAccessToken = useSelector(selectDelegatedAccessToken);
const delegationAccessToken = useSelector(selectDelegationAccessToken);
// popup制御関係
const [islicenseOrderPopupOpen, setIslicenseOrderPopupOpen] = useState(false);
@ -102,16 +102,10 @@ export const LicenseSummary: React.FC<LicenseSummaryProps> = (
{!islicenseOrderHistoryOpen && (
<div
className={`${styles.wrap} ${
delegatedAccessToken ? styles.manage : ""
delegationAccessToken ? styles.manage : ""
}`}
>
{
// 代行操作中の場合は、代行操作バーを表示する
// TODO 代行操作中の会社名と、代行操作用のトークンを取得があれば表示するという風にする(別タスクで)
delegatedAccessToken && (
<DelegationBar delegatedCompanyName="XXXXXX" />
)
}
{delegationAccessToken && <DelegationBar />}
<Header />
<UpdateTokenTimer />

View File

@ -1,7 +1,7 @@
import { useMsal } from "@azure/msal-react";
import { AppDispatch } from "app/store";
import { isIdToken } from "common/token";
import { loadAccessToken, loadRefreshToken } from "features/auth/utils";
import { loadAccessToken, loadRefreshToken } from "features/auth";
import { loginAsync, selectLocalStorageKeyforIdToken } from "features/login";
import React, { useCallback, useEffect } from "react";
import Footer from "components/footer";

View File

@ -6,7 +6,7 @@ import Header from "components/header";
import React, { useCallback, useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import styles from "styles/app.module.scss";
import { isApproveTier } from "features/auth/utils";
import { isApproveTier } from "features/auth";
import {
LIMIT_PARTNER_VIEW_NUM,
selectCurrentPage,
@ -17,9 +17,14 @@ import {
getPartnerInfoAsync,
selectPartnersInfo,
} from "features/partner/index";
import { savePageInfo } from "features/partner/partnerSlice";
import {
changeDelegateAccount,
savePageInfo,
} from "features/partner/partnerSlice";
import { getTranslationID } from "translation";
import { useTranslation } from "react-i18next";
import { getDelegationTokenAsync } from "features/auth/operations";
import { useNavigate } from "react-router-dom";
import personAdd from "../../assets/images/person_add.svg";
import { TIERS } from "../../components/auth/constants";
import { AddPartnerAccountPopup } from "./addPartnerAccountPopup";
@ -29,6 +34,7 @@ const PartnerPage: React.FC = (): JSX.Element => {
const dispatch: AppDispatch = useDispatch();
const [isPopupOpen, setIsPopupOpen] = useState(false);
const [t] = useTranslation();
const navigate = useNavigate();
const total = useSelector(selectTotal);
const totalPage = useSelector(selectTotalPage);
const offset = useSelector(selectOffset);
@ -83,6 +89,19 @@ const PartnerPage: React.FC = (): JSX.Element => {
savePageInfo({ limit: LIMIT_PARTNER_VIEW_NUM, offset: targetOffset })
);
};
// 代理操作開始処理
const startDealerManagement = useCallback(
async (delegatedAccountId: number, delegatedCompanyName: string) => {
dispatch(
changeDelegateAccount({ delegatedAccountId, delegatedCompanyName })
);
const { meta } = await dispatch(getDelegationTokenAsync());
if (meta.requestStatus === "fulfilled") {
navigate("/user");
}
},
[dispatch, navigate]
);
// HTML
return (
@ -160,8 +179,8 @@ const PartnerPage: React.FC = (): JSX.Element => {
<tr>
<td className={styles.clm0}>
<ul className={styles.menuInTable}>
<li>
{isVisibleButton && (
{isVisibleButton && (
<li>
<a>
{t(
getTranslationID(
@ -169,8 +188,27 @@ const PartnerPage: React.FC = (): JSX.Element => {
)
)}
</a>
)}
</li>
</li>
)}
{isVisibleDealerManagement && (
<li>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */}
<a
onClick={() => {
startDealerManagement(x.accountId, x.name);
}}
className={
x.dealerManagement ? "" : styles.isDisable
}
>
{t(
getTranslationID(
"partnerPage.label.dealerManagement"
)
)}
</a>
</li>
)}
</ul>
</td>
<td>{x.name}</td>

View File

@ -14,15 +14,17 @@ import {
listTemplateAsync,
selectIsLoading,
} from "features/workflow/template";
import { selectDelegationAccessToken } from "features/auth/selectors";
import { DelegationBar } from "components/delegate";
import { selectDelegatedAccessToken } from "features/auth/selectors";
import { useNavigate } from "react-router-dom";
import { AddTemplateFilePopup } from "./addTemplateFilePopup";
export const TemplateFilePage: React.FC = () => {
const dispatch: AppDispatch = useDispatch();
const [t] = useTranslation();
// authStateに配置予定の代行操作用のトークンを取得する
const delegatedAccessToken = useSelector(selectDelegatedAccessToken);
const navigate = useNavigate();
// 代行操作用のトークンを取得する
const delegationAccessToken = useSelector(selectDelegationAccessToken);
const templates = useSelector(selectTemplates);
const isLoading = useSelector(selectIsLoading);
@ -44,16 +46,10 @@ export const TemplateFilePage: React.FC = () => {
)}
<div
className={`${styles.wrap} ${
delegatedAccessToken ? styles.manage : ""
delegationAccessToken ? styles.manage : ""
}`}
>
{
// 代行操作中の場合は、代行操作バーを表示する
// TODO 代行操作中の会社名と、代行操作用のトークンを取得があれば表示するという風にする(別タスクで)
delegatedAccessToken && (
<DelegationBar delegatedCompanyName="XXXXXX" />
)
}
{delegationAccessToken && <DelegationBar />}
<Header />
<UpdateTokenTimer />
<main className={styles.main}>
@ -71,8 +67,9 @@ export const TemplateFilePage: React.FC = () => {
<div>
<ul className={`${styles.menuAction} ${styles.worktype}`}>
<li>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */}
<a
href="/workflow"
onClick={() => navigate("/workflow")}
className={`${styles.menuLink} ${styles.isActive}`}
>
<img src={undo} alt="" className={styles.menuIcon} />

View File

@ -15,16 +15,18 @@ import {
import { AppDispatch } from "app/store";
import { useTranslation } from "react-i18next";
import { getTranslationID } from "translation";
import { selectDelegatedAccessToken } from "features/auth/selectors";
import { selectDelegationAccessToken } from "features/auth/selectors";
import { DelegationBar } from "components/delegate";
import { EditTypistGroupPopup } from "./editTypistGroupPopup";
import { useNavigate } from "react-router-dom";
import { AddTypistGroupPopup } from "./addTypistGroupPopup";
import { EditTypistGroupPopup } from "./editTypistGroupPopup";
const TypistGroupSettingPage: React.FC = (): JSX.Element => {
const dispatch: AppDispatch = useDispatch();
const [t] = useTranslation();
const navigate = useNavigate();
// 代行操作用のトークンを取得する
const delegatedAccessToken = useSelector(selectDelegatedAccessToken);
const delegationAccessToken = useSelector(selectDelegationAccessToken);
const isLoading = useSelector(selectIsLoading);
const typistGroup = useSelector(selectTypistGroups);
@ -66,16 +68,10 @@ const TypistGroupSettingPage: React.FC = (): JSX.Element => {
/>
<div
className={`${styles.wrap} ${
delegatedAccessToken ? styles.manage : ""
delegationAccessToken ? styles.manage : ""
}`}
>
{
// 代行操作中の場合は、代行操作バーを表示する
// TODO 代行操作中の会社名と、代行操作用のトークンを取得があれば表示するという風にする(別タスクで)
delegatedAccessToken && (
<DelegationBar delegatedCompanyName="XXXXXX" />
)
}
{delegationAccessToken && <DelegationBar />}
<Header />
<UpdateTokenTimer />
<main className={styles.main}>
@ -93,8 +89,9 @@ const TypistGroupSettingPage: React.FC = (): JSX.Element => {
<div>
<ul className={styles.menuAction}>
<li>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */}
<a
href="/workflow"
onClick={() => navigate("/workflow")}
className={`${styles.menuLink} ${styles.isActive}`}
>
<img src={undo} alt="" className={styles.menuIcon} />

View File

@ -15,14 +15,14 @@ import { useTranslation } from "react-i18next";
import { getTranslationID } from "translation";
import { isLicenseStatusType, UserView } from "features/user/types";
import { LICENSE_STATUS } from "features/user/constants";
import { isApproveTier } from "features/auth/utils";
import { isApproveTier } from "features/auth";
import { TIERS } from "components/auth/constants";
import {
changeUpdateUser,
changeLicenseAllocateUser,
} from "features/user/userSlice";
import { DelegationBar } from "components/delegate";
import { selectDelegatedAccessToken } from "features/auth/selectors";
import { selectDelegationAccessToken } from "features/auth/selectors";
import personAdd from "../../assets/images/person_add.svg";
import checkFill from "../../assets/images/check_fill.svg";
import checkOutline from "../../assets/images/check_outline.svg";
@ -35,7 +35,7 @@ const UserListPage: React.FC = (): JSX.Element => {
const dispatch: AppDispatch = useDispatch();
const [t] = useTranslation();
// 代行操作用のトークンを取得する
const delegatedAccessToken = useSelector(selectDelegatedAccessToken);
const delegationAccessToken = useSelector(selectDelegationAccessToken);
const [isPopupOpen, setIsPopupOpen] = useState(false);
const [isUpdatePopupOpen, setIsUpdatePopupOpen] = useState(false);
@ -112,16 +112,10 @@ const UserListPage: React.FC = (): JSX.Element => {
/>
<div
className={`${styles.wrap} ${
delegatedAccessToken ? styles.manage : ""
delegationAccessToken ? styles.manage : ""
}`}
>
{
// 代行操作中の場合は、代行操作バーを表示する
// TODO 代行操作中の会社名と、代行操作用のトークンを取得があれば表示するという風にする(別タスクで)
delegatedAccessToken && (
<DelegationBar delegatedCompanyName="XXXXXX" />
)
}
{delegationAccessToken && <DelegationBar />}
<Header />
<UpdateTokenTimer />
<main className={styles.main}>

View File

@ -21,8 +21,9 @@ import {
deleteWorktypeAsync,
} from "features/workflow/worktype";
import { AppDispatch } from "app/store";
import { selectDelegatedAccessToken } from "features/auth/selectors";
import { selectDelegationAccessToken } from "features/auth/selectors";
import { DelegationBar } from "components/delegate";
import { useNavigate } from "react-router-dom";
import { AddWorktypeIdPopup } from "./addWorktypeIdPopup";
import { EditWorktypeIdPopup } from "./editWorktypeIdPopup";
import { EditOptionItemsPopup } from "./editOptionItemsPopup";
@ -30,8 +31,9 @@ import { EditOptionItemsPopup } from "./editOptionItemsPopup";
const WorktypeIdSettingPage: React.FC = (): JSX.Element => {
const dispatch: AppDispatch = useDispatch();
const [t] = useTranslation();
const navigate = useNavigate();
// 代行操作用のトークンを取得する
const delegatedAccessToken = useSelector(selectDelegatedAccessToken);
const delegationAccessToken = useSelector(selectDelegationAccessToken);
const isLoading = useSelector(selectIsLoading);
const worktypes = useSelector(selectWorktypes);
const activeWorktypeId = useSelector(selectActiveWorktypeId);
@ -131,16 +133,10 @@ const WorktypeIdSettingPage: React.FC = (): JSX.Element => {
/>
<div
className={`${styles.wrap} ${
delegatedAccessToken ? styles.manage : ""
delegationAccessToken ? styles.manage : ""
}`}
>
{
// 代行操作中の場合は、代行操作バーを表示する
// TODO 代行操作中の会社名と、代行操作用のトークンを取得があれば表示するという風にする(別タスクで)
delegatedAccessToken && (
<DelegationBar delegatedCompanyName="XXXXXX" />
)
}
{delegationAccessToken && <DelegationBar />}
<Header />
<UpdateTokenTimer />
<main className={styles.main}>
@ -157,8 +153,9 @@ const WorktypeIdSettingPage: React.FC = (): JSX.Element => {
<div>
<ul className={`${styles.menuAction} ${styles.worktype}`}>
<li>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */}
<a
href="/workflow"
onClick={() => navigate("/workflow")}
className={`${styles.menuLink} ${styles.isActive}`}
>
<img src={undo} alt="" className={styles.menuIcon} />

View File

@ -17,23 +17,25 @@ import {
selectIsLoading,
selectWorkflows,
} from "features/workflow";
import { DelegationBar } from "components/delegate";
import { selectDelegationAccessToken } from "features/auth/selectors";
import progress_activit from "assets/images/progress_activit.svg";
import { getTranslationID } from "translation";
import { DelegationBar } from "components/delegate";
import { selectDelegatedAccessToken } from "features/auth/selectors";
import { useNavigate } from "react-router-dom";
import { EditWorkflowPopup } from "./editworkflowPopup";
import { AddWorkflowPopup } from "./addworkflowPopup";
const WorkflowPage: React.FC = (): JSX.Element => {
const dispatch: AppDispatch = useDispatch();
const [t] = useTranslation();
const navigate = useNavigate();
// 追加Popupの表示制御
const [isShowAddPopup, setIsShowAddPopup] = useState<boolean>(false);
// 編集Popupの表示制御
const [isShowEditPopup, setIsShowEditPopup] = useState<boolean>(false);
// 代行操作用のトークンを取得する
const delegatedAccessToken = useSelector(selectDelegatedAccessToken);
const delegationAccessToken = useSelector(selectDelegationAccessToken);
const workflows = useSelector(selectWorkflows);
const isLoading = useSelector(selectIsLoading);
@ -76,16 +78,10 @@ const WorkflowPage: React.FC = (): JSX.Element => {
)}
<div
className={`${styles.wrap} ${
delegatedAccessToken ? styles.manage : ""
delegationAccessToken ? styles.manage : ""
}`}
>
{
// 代行操作中の場合は、代行操作バーを表示する
// TODO 代行操作中の会社名と、代行操作用のトークンを取得があれば表示するという風にする(別タスクで)
delegatedAccessToken && (
<DelegationBar delegatedCompanyName="XXXXXX" />
)
}
{delegationAccessToken && <DelegationBar />}
<Header />
<UpdateTokenTimer />
<main className={styles.main}>
@ -115,8 +111,9 @@ const WorkflowPage: React.FC = (): JSX.Element => {
</a>
</li>
<li>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */}
<a
href="/workflow/template"
onClick={() => navigate("/workflow/template")}
className={`${styles.menuLink} ${styles.isActive}`}
>
<img
@ -130,8 +127,9 @@ const WorkflowPage: React.FC = (): JSX.Element => {
</a>
</li>
<li>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */}
<a
href="/workflow/worktype-id"
onClick={() => navigate("/workflow/worktype-id")}
className={`${styles.menuLink} ${styles.isActive}`}
>
<img
@ -145,8 +143,9 @@ const WorkflowPage: React.FC = (): JSX.Element => {
</a>
</li>
<li>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */}
<a
href="/workflow/typist-group"
onClick={() => navigate("/workflow/typist-group")}
className={`${styles.menuLink} ${styles.isActive}`}
>
<img

View File

@ -18,6 +18,13 @@
"delete": "Löschen",
"return": "zurückkehren",
"operationInsteadOf": "(de)Operation instead of:",
"headerName": "(de)ODMS Cloud",
"headerAccount": "(de)Account",
"headerUser": "(de)User",
"headerLicense": "(de)License",
"headerDictations": "(de)Dictations",
"headerWorkflow": "(de)Workflow",
"headerPartners": "(de)Partners",
"tier1": "(de)Admin",
"tier2": "(de)BC",
"tier3": "(de)Distributor",
@ -463,6 +470,11 @@
"dealerManagement": "Erlauben Sie dem Händler, Änderungen vorzunehmen",
"partners": "Partner",
"deleteAccount": "Konto löschen"
},
"message": {
"delegateNotAllowedError": "(de)パートナーの代行操作が許可されていません。画面を更新し、再度ご確認ください。",
"deleteFailedError": "(de)代行操作に失敗しました。画面を更新し、再度ご確認ください。",
"delegateCancelError": "(de)代行操作の許可が取り消されたため、代行操作を終了しました。"
}
},
"accountPage": {

View File

@ -18,6 +18,13 @@
"delete": "Delete",
"return": "Return",
"operationInsteadOf": "Operation instead of:",
"headerName": "ODMS Cloud",
"headerAccount": "Account",
"headerUser": "User",
"headerLicense": "License",
"headerDictations": "Dictations",
"headerWorkflow": "Workflow",
"headerPartners": "Partners",
"tier1": "Admin",
"tier2": "BC",
"tier3": "Distributor",
@ -463,6 +470,11 @@
"dealerManagement": "Dealer Management",
"partners": "Partners",
"deleteAccount": "Delete Account"
},
"message": {
"delegateNotAllowedError": "パートナーの代行操作が許可されていません。画面を更新し、再度ご確認ください。",
"deleteFailedError": "代行操作に失敗しました。画面を更新し、再度ご確認ください。",
"delegateCancelError": "代行操作の許可が取り消されたため、代行操作を終了しました。"
}
},
"accountPage": {

View File

@ -18,6 +18,13 @@
"delete": "Delete",
"return": "Devolver",
"operationInsteadOf": "(es)Operation instead of:",
"headerName": "(es)ODMS Cloud",
"headerAccount": "(es)Account",
"headerUser": "(es)User",
"headerLicense": "(es)License",
"headerDictations": "(es)Dictations",
"headerWorkflow": "(es)Workflow",
"headerPartners": "(es)Partners",
"tier1": "(es)Admin",
"tier2": "(es)BC",
"tier3": "(es)Distributor",
@ -463,6 +470,11 @@
"dealerManagement": "Permitir que el distribuidor realice los cambios",
"partners": "Socios",
"deleteAccount": "Borrar cuenta"
},
"message": {
"delegateNotAllowedError": "(es)パートナーの代行操作が許可されていません。画面を更新し、再度ご確認ください。",
"deleteFailedError": "(es)代行操作に失敗しました。画面を更新し、再度ご確認ください。",
"delegateCancelError": "(es)代行操作の許可が取り消されたため、代行操作を終了しました。"
}
},
"accountPage": {

View File

@ -18,6 +18,13 @@
"delete": "Delete",
"return": "Retour",
"operationInsteadOf": "(fr)Operation instead of:",
"headerName": "(fr)ODMS Cloud",
"headerAccount": "(fr)Account",
"headerUser": "(fr)User",
"headerLicense": "(fr)License",
"headerDictations": "(fr)Dictations",
"headerWorkflow": "(fr)Workflow",
"headerPartners": "(fr)Partners",
"tier1": "(fr)Admin",
"tier2": "(fr)BC",
"tier3": "(fr)Distributor",
@ -463,6 +470,11 @@
"dealerManagement": "Autoriser le revendeur à modifier les paramètres",
"partners": "Partenaires",
"deleteAccount": "Supprimer le compte"
},
"message": {
"delegateNotAllowedError": "(fr)パートナーの代行操作が許可されていません。画面を更新し、再度ご確認ください。",
"deleteFailedError": "(fr)代行操作に失敗しました。画面を更新し、再度ご確認ください。",
"delegateCancelError": "(fr)代行操作の許可が取り消されたため、代行操作を終了しました。"
}
},
"accountPage": {

View File

@ -0,0 +1 @@
local.settings.json

View File

@ -0,0 +1,38 @@
# To enable ssh & remote debugging on app service change the base image to the one below
# FROM mcr.microsoft.com/azure-functions/node:4-node18-appservice
# FROM mcr.microsoft.com/azure-functions/node:4-node18
FROM node:18.17.1-buster
RUN /bin/cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && \
echo "Asia/Tokyo" > /etc/timezone
# Options for setup script
ARG INSTALL_ZSH="true"
ARG UPGRADE_PACKAGES="false"
ARG USERNAME=vscode
# 1000 はnodeで使われているためずらす
ARG USER_UID=1001
ARG USER_GID=$USER_UID
ENV AzureWebJobsScriptRoot=/home/site/wwwroot \
AzureFunctionsJobHost__Logging__Console__IsEnabled=true
# Install needed packages and setup non-root user. Use a separate RUN statement to add your own dependencies.
COPY library-scripts/common-debian.sh /tmp/library-scripts/
RUN bash /tmp/library-scripts/common-debian.sh "${INSTALL_ZSH}" "${USERNAME}" "${USER_UID}" "${USER_GID}" "${UPGRADE_PACKAGES}" \
&& apt-get install default-jre -y \
&& apt-get clean -y && rm -rf /var/lib/apt/lists/* /tmp/library-scripts
# Update NPM
RUN npm install -g npm
# 以下 ユーザー権限で実施
USER $USERNAME
# copy init-script
COPY --chown=$USERNAME:$USERNAME init.sh /home/${USERNAME}/
RUN chmod +x /home/${USERNAME}/init.sh
# 初期化を行う
# node imageのデフォルトENTRYPOINTが邪魔するため上書き
ENTRYPOINT /home/vscode/init.sh

View File

@ -0,0 +1,57 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.209.6/containers/javascript-node
{
"name": "Dev Dictation Function",
"dockerComposeFile": [
"docker-compose.yml"
],
"service": "dictation_function",
//
"shutdownAction": "none",
"workspaceFolder": "/app/dictation_function",
"runArgs": [
"--cap-add=SYS_PTRACE",
"--security-opt",
"seccomp=unconfined"
],
// Set *default* container specific settings.json values on container create.
"settings": {
"terminal.integrated.shell.linux": "/bin/bash",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"eslint.format.enable": false,
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
// formatter
"editor.formatOnPaste": true,
"editor.formatOnType": true,
"editor.renderWhitespace": "all",
"editor.insertSpaces": false,
"editor.renderLineHighlight": "all"
},
// Add the IDs of extensions you want installed when the container is created.
"extensions": [
"dbaeumer.vscode-eslint",
"salbert.comment-ts",
"gruntfuggly.todo-tree",
"esbenp.prettier-vscode",
"ms-vsliveshare.vsliveshare",
"albymor.increment-selection",
"eamodio.gitlens",
"wmaurer.change-case",
"Azurite.azurite"
],
// Use 'postCreateCommand' to run commands after the container is created.
// "postCreateCommand": "yarn install",
"postCreateCommand": "sudo chown -R vscode:vscode /app/dictation_function",
// Uncomment to connect as a non-root user. See https://aka.ms/vscode-remote/containers/non-root.
"remoteUser": "vscode"
}

View File

@ -5,8 +5,8 @@ services:
build: .
working_dir: /app/dictation_function
ports:
- "3001:3001"
- "6007:6007"
- "80:80"
- "8082:8082"
volumes:
- ../../:/app
- node_modules:/app/dictation_function/node_modules

View File

@ -0,0 +1,20 @@
#!/bin/bash
#
# Init Script for function Container
#
echo [init.sh] dictation_function initialize.
# /app の権限がデフォルトでは node ユーザーになっているため、
# 権限確認し、vscode ユーザでない場合付け替える
ls -ld /app | grep vscode
if [ $? -ne 0 ]; then
echo [init.sh] change /app owner to vscode.
sudo chown -R vscode:vscode /app
fi
cd /app/dictation_function
echo [init.sh] initialize completed!
sleep infinity

View File

@ -0,0 +1,190 @@
#!/usr/bin/env bash
#-------------------------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information.
#-------------------------------------------------------------------------------------------------------------
# Syntax: ./common-debian.sh [install zsh flag] [username] [user UID] [user GID] [upgrade packages flag]
INSTALL_ZSH=${1:-"true"}
USERNAME=${2:-"vscode"}
USER_UID=${3:-1000}
USER_GID=${4:-1000}
UPGRADE_PACKAGES=${5:-"true"}
set -e
if [ "$(id -u)" -ne 0 ]; then
echo -e 'Script must be run a root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.'
exit 1
fi
# Treat a user name of "none" as root
if [ "${USERNAME}" = "none" ] || [ "${USERNAME}" = "root" ]; then
USERNAME=root
USER_UID=0
USER_GID=0
fi
# Load markers to see which steps have already run
MARKER_FILE="/usr/local/etc/vscode-dev-containers/common"
if [ -f "${MARKER_FILE}" ]; then
echo "Marker file found:"
cat "${MARKER_FILE}"
source "${MARKER_FILE}"
fi
# Ensure apt is in non-interactive to avoid prompts
export DEBIAN_FRONTEND=noninteractive
# Function to call apt-get if needed
apt-get-update-if-needed()
{
if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then
echo "Running apt-get update..."
apt-get update
else
echo "Skipping apt-get update."
fi
}
# Run install apt-utils to avoid debconf warning then verify presence of other common developer tools and dependencies
if [ "${PACKAGES_ALREADY_INSTALLED}" != "true" ]; then
apt-get-update-if-needed
PACKAGE_LIST="apt-utils \
git \
openssh-client \
less \
iproute2 \
procps \
curl \
wget \
unzip \
zip \
nano \
jq \
lsb-release \
ca-certificates \
apt-transport-https \
dialog \
gnupg2 \
libc6 \
libgcc1 \
libgssapi-krb5-2 \
libicu[0-9][0-9] \
liblttng-ust0 \
libstdc++6 \
zlib1g \
locales \
sudo"
# Install libssl1.1 if available
if [[ ! -z $(apt-cache --names-only search ^libssl1.1$) ]]; then
PACKAGE_LIST="${PACKAGE_LIST} libssl1.1"
fi
# Install appropriate version of libssl1.0.x if available
LIBSSL=$(dpkg-query -f '${db:Status-Abbrev}\t${binary:Package}\n' -W 'libssl1\.0\.?' 2>&1 || echo '')
if [ "$(echo "$LIBSSL" | grep -o 'libssl1\.0\.[0-9]:' | uniq | sort | wc -l)" -eq 0 ]; then
if [[ ! -z $(apt-cache --names-only search ^libssl1.0.2$) ]]; then
# Debian 9
PACKAGE_LIST="${PACKAGE_LIST} libssl1.0.2"
elif [[ ! -z $(apt-cache --names-only search ^libssl1.0.0$) ]]; then
# Ubuntu 18.04, 16.04, earlier
PACKAGE_LIST="${PACKAGE_LIST} libssl1.0.0"
fi
fi
echo "Packages to verify are installed: ${PACKAGE_LIST}"
apt-get -y install --no-install-recommends ${PACKAGE_LIST} 2> >( grep -v 'debconf: delaying package configuration, since apt-utils is not installed' >&2 )
PACKAGES_ALREADY_INSTALLED="true"
fi
# Get to latest versions of all packages
if [ "${UPGRADE_PACKAGES}" = "true" ]; then
apt-get-update-if-needed
apt-get -y upgrade --no-install-recommends
apt-get autoremove -y
fi
# Ensure at least the en_US.UTF-8 UTF-8 locale is available.
# Common need for both applications and things like the agnoster ZSH theme.
if [ "${LOCALE_ALREADY_SET}" != "true" ]; then
echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen
locale-gen
LOCALE_ALREADY_SET="true"
fi
# Create or update a non-root user to match UID/GID - see https://aka.ms/vscode-remote/containers/non-root-user.
if id -u $USERNAME > /dev/null 2>&1; then
# User exists, update if needed
if [ "$USER_GID" != "$(id -G $USERNAME)" ]; then
groupmod --gid $USER_GID $USERNAME
usermod --gid $USER_GID $USERNAME
fi
if [ "$USER_UID" != "$(id -u $USERNAME)" ]; then
usermod --uid $USER_UID $USERNAME
fi
else
# Create user
groupadd --gid $USER_GID $USERNAME
useradd -s /bin/bash --uid $USER_UID --gid $USER_GID -m $USERNAME
fi
# Add add sudo support for non-root user
if [ "${USERNAME}" != "root" ] && [ "${EXISTING_NON_ROOT_USER}" != "${USERNAME}" ]; then
echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME
chmod 0440 /etc/sudoers.d/$USERNAME
EXISTING_NON_ROOT_USER="${USERNAME}"
fi
# .bashrc/.zshrc snippet
RC_SNIPPET="$(cat << EOF
export USER=\$(whoami)
export PATH=\$PATH:\$HOME/.local/bin
if [[ \$(which code-insiders 2>&1) && ! \$(which code 2>&1) ]]; then
alias code=code-insiders
fi
EOF
)"
# Ensure ~/.local/bin is in the PATH for root and non-root users for bash. (zsh is later)
if [ "${RC_SNIPPET_ALREADY_ADDED}" != "true" ]; then
echo "${RC_SNIPPET}" | tee -a /root/.bashrc >> /etc/skel/.bashrc
if [ "${USERNAME}" != "root" ]; then
echo "${RC_SNIPPET}" >> /home/$USERNAME/.bashrc
chown $USER_UID:$USER_GID /home/$USERNAME/.bashrc
fi
RC_SNIPPET_ALREADY_ADDED="true"
fi
# Optionally install and configure zsh
if [ "${INSTALL_ZSH}" = "true" ] && [ ! -d "/root/.oh-my-zsh" ] && [ "${ZSH_ALREADY_INSTALLED}" != "true" ]; then
apt-get-update-if-needed
apt-get install -y zsh
curl -fsSLo- https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh | bash 2>&1
echo -e "${RC_SNIPPET}\nDEFAULT_USER=\$USER\nprompt_context(){}" >> /root/.zshrc
cp -fR /root/.oh-my-zsh /etc/skel
cp -f /root/.zshrc /etc/skel
sed -i -e "s/\/root\/.oh-my-zsh/\/home\/\$(whoami)\/.oh-my-zsh/g" /etc/skel/.zshrc
if [ "${USERNAME}" != "root" ]; then
cp -fR /etc/skel/.oh-my-zsh /etc/skel/.zshrc /home/$USERNAME
chown -R $USER_UID:$USER_GID /home/$USERNAME/.oh-my-zsh /home/$USERNAME/.zshrc
fi
ZSH_ALREADY_INSTALLED="true"
fi
# Write marker file
mkdir -p "$(dirname "${MARKER_FILE}")"
echo -e "\
PACKAGES_ALREADY_INSTALLED=${PACKAGES_ALREADY_INSTALLED}\n\
LOCALE_ALREADY_SET=${LOCALE_ALREADY_SET}\n\
EXISTING_NON_ROOT_USER=${EXISTING_NON_ROOT_USER}\n\
RC_SNIPPET_ALREADY_ADDED=${RC_SNIPPET_ALREADY_ADDED}\n\
ZSH_ALREADY_INSTALLED=${ZSH_ALREADY_INSTALLED}" > "${MARKER_FILE}"
echo "Done!"

View File

@ -0,0 +1,133 @@
#!/usr/bin/env bash
#-------------------------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information.
#-------------------------------------------------------------------------------------------------------------
#
# Docs: https://github.com/microsoft/vscode-dev-containers/blob/master/script-library/docs/go.md
#
# Syntax: ./go-debian.sh [Go version] [GOROOT] [GOPATH] [non-root user] [Add GOPATH, GOROOT to rc files flag] [Install tools flag]
TARGET_GO_VERSION=${1:-"latest"}
TARGET_GOROOT=${2:-"/usr/local/go"}
TARGET_GOPATH=${3:-"/go"}
USERNAME=${4:-"automatic"}
UPDATE_RC=${5:-"true"}
INSTALL_GO_TOOLS=${6:-"true"}
set -e
if [ "$(id -u)" -ne 0 ]; then
echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.'
exit 1
fi
# Ensure that login shells get the correct path if the user updated the PATH using ENV.
rm -f /etc/profile.d/00-restore-env.sh
echo "export PATH=${PATH//$(sh -lc 'echo $PATH')/\$PATH}" > /etc/profile.d/00-restore-env.sh
chmod +x /etc/profile.d/00-restore-env.sh
# Determine the appropriate non-root user
if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then
USERNAME=""
POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)")
for CURRENT_USER in ${POSSIBLE_USERS[@]}; do
if id -u ${CURRENT_USER} > /dev/null 2>&1; then
USERNAME=${CURRENT_USER}
break
fi
done
if [ "${USERNAME}" = "" ]; then
USERNAME=root
fi
elif [ "${USERNAME}" = "none" ] || ! id -u ${USERNAME} > /dev/null 2>&1; then
USERNAME=root
fi
function updaterc() {
if [ "${UPDATE_RC}" = "true" ]; then
echo "Updating /etc/bash.bashrc and /etc/zsh/zshrc..."
echo -e "$1" >> /etc/bash.bashrc
if [ -f "/etc/zsh/zshrc" ]; then
echo -e "$1" >> /etc/zsh/zshrc
fi
fi
}
export DEBIAN_FRONTEND=noninteractive
# Install curl, tar, git, other dependencies if missing
if ! dpkg -s curl ca-certificates tar git g++ gcc libc6-dev make pkg-config > /dev/null 2>&1; then
if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then
apt-get update
fi
apt-get -y install --no-install-recommends curl ca-certificates tar git g++ gcc libc6-dev make pkg-config
fi
# Get latest version number if latest is specified
if [ "${TARGET_GO_VERSION}" = "latest" ] || [ "${TARGET_GO_VERSION}" = "current" ] || [ "${TARGET_GO_VERSION}" = "lts" ]; then
TARGET_GO_VERSION=$(curl -sSL "https://golang.org/VERSION?m=text" | sed -n '/^go/s///p' )
fi
# Install Go
GO_INSTALL_SCRIPT="$(cat <<EOF
set -e
echo "Downloading Go ${TARGET_GO_VERSION}..."
curl -sSL -o /tmp/go.tar.gz "https://golang.org/dl/go${TARGET_GO_VERSION}.linux-amd64.tar.gz"
echo "Extracting Go ${TARGET_GO_VERSION}..."
tar -xzf /tmp/go.tar.gz -C "${TARGET_GOROOT}" --strip-components=1
rm -f /tmp/go.tar.gz
EOF
)"
if [ "${TARGET_GO_VERSION}" != "none" ] && ! type go > /dev/null 2>&1; then
mkdir -p "${TARGET_GOROOT}" "${TARGET_GOPATH}"
chown -R ${USERNAME} "${TARGET_GOROOT}" "${TARGET_GOPATH}"
su ${USERNAME} -c "${GO_INSTALL_SCRIPT}"
else
echo "Go already installed. Skipping."
fi
# Install Go tools that are isImportant && !replacedByGopls based on
# https://github.com/golang/vscode-go/blob/0c6dce4a96978f61b022892c1376fe3a00c27677/src/goTools.ts#L188
# exception: golangci-lint is installed using their install script below.
# バージョン指定だとインストールがこけるため、latestを指定。
GO_TOOLS="\
golang.org/x/tools/gopls@latest \
honnef.co/go/tools/...@latest \
golang.org/x/lint/golint@latest \
github.com/mgechev/revive@latest \
github.com/uudashr/gopkgs/v2/cmd/gopkgs@latest \
github.com/ramya-rao-a/go-outline@latest \
github.com/go-delve/delve/cmd/dlv@latest \
github.com/golangci/golangci-lint/cmd/golangci-lint@latest \
github.com/cweill/gotests/...@latest \
github.com/davidrjenni/reftools/cmd/fillstruct@latest "
if [ "${INSTALL_GO_TOOLS}" = "true" ]; then
echo "Installing common Go tools..."
export PATH=${TARGET_GOROOT}/bin:${PATH}
echo `PATH=${TARGET_GOROOT}/bin:${PATH}`
mkdir -p /tmp/gotools /usr/local/etc/vscode-dev-containers ${TARGET_GOPATH}/bin
cd /tmp/gotools
export GOPATH=/tmp/gotools
export GOCACHE=/tmp/gotools/cache
# Go tools w/module support
export GO111MODULE=on
(echo "${GO_TOOLS}" | xargs -n 1 go install -v )2>&1 | tee -a /usr/local/etc/vscode-dev-containers/go.log
# Move Go tools into path and clean up
mv /tmp/gotools/bin/* ${TARGET_GOPATH}/bin/
rm -rf /tmp/gotools
chown -R ${USERNAME} "${TARGET_GOPATH}"
fi
# Add GOPATH variable and bin directory into PATH in bashrc/zshrc files (unless disabled)
updaterc "$(cat << EOF
export GOPATH="${TARGET_GOPATH}"
if [[ "\${PATH}" != *"\${GOPATH}/bin"* ]]; then export PATH="\${PATH}:\${GOPATH}/bin"; fi
export GOROOT="${TARGET_GOROOT}"
if [[ "\${PATH}" != *"\${GOROOT}/bin"* ]]; then export PATH="\${PATH}:\${GOROOT}/bin"; fi
EOF
)"
echo "Done!"

View File

@ -7,7 +7,7 @@
"": {
"version": "1.0.0",
"dependencies": {
"@azure/functions": "^4.0.0-alpha.9"
"@azure/functions": "^4.0.0"
},
"devDependencies": {
"@types/node": "18.x",

View File

@ -12,7 +12,7 @@
"test": "echo \"No tests yet...\""
},
"dependencies": {
"@azure/functions": "^4.0.0-alpha.9"
"@azure/functions": "^4.0.0"
},
"devDependencies": {
"azure-functions-core-tools": "^4.x",
@ -20,4 +20,4 @@
"typescript": "^4.0.0",
"rimraf": "^5.0.0"
}
}
}

View File

@ -1,15 +0,0 @@
import { app, HttpRequest, HttpResponseInit, InvocationContext } from "@azure/functions";
export async function HttpExample(request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> {
context.log(`Http function processed request for url "${request.url}"`);
const name = request.query.get('name') || await request.text() || 'world';
return { body: `Hello, ${name}!` };
};
app.http('HttpExample', {
methods: ['GET', 'POST'],
authLevel: 'anonymous',
handler: HttpExample
});

View File

@ -0,0 +1,15 @@
import { app, InvocationContext, Timer } from "@azure/functions";
// タイマートリガー処理のサンプルです
// TODO:開発が進んだら削除すること
export async function timerTriggerExample(
myTimer: Timer,
context: InvocationContext
): Promise<void> {
context.log("Timer function processed request.");
}
app.timer("timerTriggerExample", {
schedule: "0 */1 * * * *",
handler: timerTriggerExample,
});