Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
ace7a16
empty commit to run pdf-converter release
arafubeatbox Nov 6, 2025
66921b1
Merge pull request #10481 from growilabs/rerun-pdf-converter-release-…
arafubeatbox Nov 6, 2025
f6d69c6
revert to 1.1.2 to re-run release
arafubeatbox Nov 6, 2025
67976f9
Merge pull request #10482 from growilabs/rerun-pdf-converter-release-…
arafubeatbox Nov 6, 2025
bcec225
enable puppeteer config from env var
arafubeatbox Nov 17, 2025
dfddcfc
increment pdf-converter version
arafubeatbox Nov 17, 2025
dd75402
disable configuration of puppeteer options
arafubeatbox Nov 17, 2025
861319a
Bump version
Nov 18, 2025
8570ff7
Merge pull request #10518 from growilabs/support/prepare-v7.3.7-RC.0
mergify[bot] Nov 18, 2025
b350bf8
Merge pull request #10516 from growilabs/feat/174344-174345-enable-pu…
yuki-takei Nov 18, 2025
e8715e7
Merge branch 'release/pdf-converter/current' of https://github.com/gr…
arafubeatbox Nov 19, 2025
2f9f0b6
Merge pull request #10524 from growilabs/pdf-converter-1.2.0
arafubeatbox Nov 19, 2025
ea352a8
Bump version
Nov 19, 2025
c963b15
Merge pull request #10525 from growilabs/support/prepare-v1.2.1-RC.0
mergify[bot] Nov 19, 2025
d8d6909
imprv: #174504 add loading spinner to admin home
riona-k Nov 21, 2025
dfc2a48
imprv:#174504 add loading spinner to admin home
riona-k Nov 21, 2025
ded5a0d
feat: add loading spinner for lists
riona-k Nov 25, 2025
4f6f908
fix: allow envVars to be optional in EnvVarsTable
riona-k Nov 25, 2025
81584bb
fix: mark envVars as optional and handle undefined safely
riona-k Nov 25, 2025
377f33e
refactor: streamline SAML settings update process by consolidating st…
yuki-takei Nov 25, 2025
59189bd
refactor: consolidate LDAP setting updates into a single method
yuki-takei Nov 25, 2025
87c935c
refactor: simplify OIDC settings update by consolidating state change…
yuki-takei Nov 25, 2025
68b2564
refactor: enhance security settings update by incorporating form data…
yuki-takei Nov 25, 2025
d7ff405
refactor: update security setting methods to accept form data for imp…
yuki-takei Nov 25, 2025
0e26b3c
Merge pull request #10540 from growilabs/fix/admin-form-degradation
mergify[bot] Nov 25, 2025
02ef08c
Merge pull request #10534 from growilabs/feature/add-loading-admin-home
yuki-takei Nov 25, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@growi/app",
"version": "7.3.6",
"version": "7.3.7-RC.0",
"license": "MIT",
"private": "true",
"scripts": {
Expand Down
5 changes: 3 additions & 2 deletions apps/app/src/client/components/Admin/AdminHome/AdminHome.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { withUnstatedContainers } from '../../UnstatedUtils';
import { EnvVarsTable } from './EnvVarsTable';
import SystemInfomationTable from './SystemInfomationTable';


const logger = loggerFactory('growi:admin');

const AdminHome = (props) => {
Expand Down Expand Up @@ -59,7 +60,7 @@ const AdminHome = (props) => {
)
}
{
// Alert message will be displayed in case that V5 migration has not been compleated
// Alert message will be displayed in case that V5 migration has not been compleated
(migrationStatus != null && !migrationStatus.isV5Compatible)
&& (
<div className={`alert ${migrationStatus.isV5Compatible == null ? 'alert-warning' : 'alert-info'}`}>
Expand Down Expand Up @@ -90,7 +91,7 @@ const AdminHome = (props) => {
<p>{t('admin:admin_top.env_var_priority')}</p>
{/* eslint-disable-next-line react/no-danger */}
<p dangerouslySetInnerHTML={{ __html: t('admin:admin_top.about_security') }} />
{adminHomeContainer.state.envVars && <EnvVarsTable envVars={adminHomeContainer.state.envVars} />}
<EnvVarsTable envVars={adminHomeContainer.state.envVars} />
</div>
</div>

Expand Down
11 changes: 9 additions & 2 deletions apps/app/src/client/components/Admin/AdminHome/EnvVarsTable.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
import React, { type JSX } from 'react';

import { LoadingSpinner } from '@growi/ui/dist/components';

type EnvVarsTableProps = {
envVars: Record<string, string | number | boolean>,
envVars?: Record<string, string | number | boolean>,
}

export const EnvVarsTable: React.FC<EnvVarsTableProps> = (props: EnvVarsTableProps) => {
const { envVars } = props;
if (envVars == null) {
return <LoadingSpinner />;
}

const envVarRows: JSX.Element[] = [];

for (const [key, value] of Object.entries(props.envVars)) {
for (const [key, value] of Object.entries(envVars ?? {})) {
if (value != null) {
envVarRows.push(
<tr key={key}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import React from 'react';

import { LoadingSpinner } from '@growi/ui/dist/components';

import AdminHomeContainer from '~/client/services/AdminHomeContainer';

import { withUnstatedContainers } from '../../UnstatedUtils';
Expand All @@ -17,7 +19,7 @@ const SystemInformationTable = (props: Props) => {
} = adminHomeContainer.state;

if (growiVersion == null || nodeVersion == null || npmVersion == null || pnpmVersion == null) {
return <></>;
return <LoadingSpinner />;
}

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,11 @@ const GitHubSecurityManagementContents = (props) => {

const onClickSubmit = useCallback(async(data) => {
try {
await adminGitHubSecurityContainer.changeGitHubClientId(data.githubClientId ?? '');
await adminGitHubSecurityContainer.changeGitHubClientSecret(data.githubClientSecret ?? '');
await adminGitHubSecurityContainer.updateGitHubSetting();
await adminGitHubSecurityContainer.updateGitHubSetting({
githubClientId: data.githubClientId ?? '',
githubClientSecret: data.githubClientSecret ?? '',
isSameUsernameTreatedAsIdenticalUser: adminGitHubSecurityContainer.state.isSameUsernameTreatedAsIdenticalUser,
});
await adminGeneralSecurityContainer.retrieveSetupStratedies();
toastSuccess(t('security_settings.OAuth.GitHub.updated_github'));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@ const GoogleSecurityManagementContents = (props) => {

const onClickSubmit = useCallback(async(data) => {
try {
await adminGoogleSecurityContainer.changeGoogleClientId(data.googleClientId ?? '');
await adminGoogleSecurityContainer.changeGoogleClientSecret(data.googleClientSecret ?? '');
await adminGoogleSecurityContainer.updateGoogleSetting();
await adminGoogleSecurityContainer.updateGoogleSetting({
googleClientId: data.googleClientId ?? '',
googleClientSecret: data.googleClientSecret ?? '',
isSameEmailTreatedAsIdenticalUser: adminGoogleSecurityContainer.state.isSameEmailTreatedAsIdenticalUser,
});
await adminGeneralSecurityContainer.retrieveSetupStratedies();
toastSuccess(t('security_settings.OAuth.Google.updated_google'));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,17 +56,20 @@ const LdapSecuritySettingContents = (props: Props) => {

const onSubmit = useCallback(async(data) => {
try {
await adminLdapSecurityContainer.changeServerUrl(data.serverUrl);
await adminLdapSecurityContainer.changeBindDN(data.ldapBindDN);
await adminLdapSecurityContainer.changeBindDNPassword(data.ldapBindDNPassword);
await adminLdapSecurityContainer.changeSearchFilter(data.ldapSearchFilter);
await adminLdapSecurityContainer.changeAttrMapUsername(data.ldapAttrMapUsername);
await adminLdapSecurityContainer.changeAttrMapMail(data.ldapAttrMapMail);
await adminLdapSecurityContainer.changeAttrMapName(data.ldapAttrMapName);
await adminLdapSecurityContainer.changeGroupSearchBase(data.ldapGroupSearchBase);
await adminLdapSecurityContainer.changeGroupSearchFilter(data.ldapGroupSearchFilter);
await adminLdapSecurityContainer.changeGroupDnProperty(data.ldapGroupDnProperty);
await adminLdapSecurityContainer.updateLdapSetting();
await adminLdapSecurityContainer.updateLdapSetting({
serverUrl: data.serverUrl,
isUserBind: adminLdapSecurityContainer.state.isUserBind,
ldapBindDN: data.ldapBindDN,
ldapBindDNPassword: data.ldapBindDNPassword,
ldapSearchFilter: data.ldapSearchFilter,
ldapAttrMapUsername: data.ldapAttrMapUsername,
isSameUsernameTreatedAsIdenticalUser: adminLdapSecurityContainer.state.isSameUsernameTreatedAsIdenticalUser,
ldapAttrMapMail: data.ldapAttrMapMail,
ldapAttrMapName: data.ldapAttrMapName,
ldapGroupSearchBase: data.ldapGroupSearchBase,
ldapGroupSearchFilter: data.ldapGroupSearchFilter,
ldapGroupDnProperty: data.ldapGroupDnProperty,
});
await adminGeneralSecurityContainer.retrieveSetupStratedies();
toastSuccess(t('security_settings.ldap.updated_ldap'));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,12 @@ const LocalSecuritySettingContents = (props: Props): JSX.Element => {

const onSubmit = useCallback(async(data) => {
try {
await adminLocalSecurityContainer.changeRegistrationWhitelist(data.registrationWhitelist);
await adminLocalSecurityContainer.updateLocalSecuritySetting();
await adminLocalSecurityContainer.updateLocalSecuritySetting({
registrationMode: adminLocalSecurityContainer.state.registrationMode,
registrationWhitelist: data.registrationWhitelist.split('\n'),
isPasswordResetEnabled: adminLocalSecurityContainer.state.isPasswordResetEnabled,
isEmailAuthenticationEnabled: adminLocalSecurityContainer.state.isEmailAuthenticationEnabled,
});
await adminGeneralSecurityContainer.retrieveSetupStratedies();
toastSuccess(t('security_settings.updated_general_security_setting'));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,23 +68,26 @@ const OidcSecurityManagementContents = (props: Props) => {

const onSubmit = useCallback(async(data) => {
try {
await adminOidcSecurityContainer.changeOidcProviderName(data.oidcProviderName);
await adminOidcSecurityContainer.changeOidcIssuerHost(data.oidcIssuerHost);
await adminOidcSecurityContainer.changeOidcClientId(data.oidcClientId);
await adminOidcSecurityContainer.changeOidcClientSecret(data.oidcClientSecret);
await adminOidcSecurityContainer.changeOidcAuthorizationEndpoint(data.oidcAuthorizationEndpoint);
await adminOidcSecurityContainer.changeOidcTokenEndpoint(data.oidcTokenEndpoint);
await adminOidcSecurityContainer.changeOidcRevocationEndpoint(data.oidcRevocationEndpoint);
await adminOidcSecurityContainer.changeOidcIntrospectionEndpoint(data.oidcIntrospectionEndpoint);
await adminOidcSecurityContainer.changeOidcUserInfoEndpoint(data.oidcUserInfoEndpoint);
await adminOidcSecurityContainer.changeOidcEndSessionEndpoint(data.oidcEndSessionEndpoint);
await adminOidcSecurityContainer.changeOidcRegistrationEndpoint(data.oidcRegistrationEndpoint);
await adminOidcSecurityContainer.changeOidcJWKSUri(data.oidcJWKSUri);
await adminOidcSecurityContainer.changeOidcAttrMapId(data.oidcAttrMapId);
await adminOidcSecurityContainer.changeOidcAttrMapUserName(data.oidcAttrMapUserName);
await adminOidcSecurityContainer.changeOidcAttrMapName(data.oidcAttrMapName);
await adminOidcSecurityContainer.changeOidcAttrMapEmail(data.oidcAttrMapEmail);
await adminOidcSecurityContainer.updateOidcSetting();
await adminOidcSecurityContainer.updateOidcSetting({
oidcProviderName: data.oidcProviderName,
oidcIssuerHost: data.oidcIssuerHost,
oidcClientId: data.oidcClientId,
oidcClientSecret: data.oidcClientSecret,
oidcAuthorizationEndpoint: data.oidcAuthorizationEndpoint,
oidcTokenEndpoint: data.oidcTokenEndpoint,
oidcRevocationEndpoint: data.oidcRevocationEndpoint,
oidcIntrospectionEndpoint: data.oidcIntrospectionEndpoint,
oidcUserInfoEndpoint: data.oidcUserInfoEndpoint,
oidcEndSessionEndpoint: data.oidcEndSessionEndpoint,
oidcRegistrationEndpoint: data.oidcRegistrationEndpoint,
oidcJWKSUri: data.oidcJWKSUri,
oidcAttrMapId: data.oidcAttrMapId,
oidcAttrMapUserName: data.oidcAttrMapUserName,
oidcAttrMapName: data.oidcAttrMapName,
oidcAttrMapEmail: data.oidcAttrMapEmail,
isSameUsernameTreatedAsIdenticalUser: adminOidcSecurityContainer.state.isSameUsernameTreatedAsIdenticalUser,
isSameEmailTreatedAsIdenticalUser: adminOidcSecurityContainer.state.isSameEmailTreatedAsIdenticalUser,
});
await adminGeneralSecurityContainer.retrieveSetupStratedies();
toastSuccess(t('security_settings.OAuth.OIDC.updated_oidc'));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,18 +46,20 @@ const SamlSecurityManagementContents = (props: Props) => {
}, [adminSamlSecurityContainer.state, reset]);

const onSubmit = useCallback(async(data) => {
adminSamlSecurityContainer.changeSamlEntryPoint(data.samlEntryPoint);
adminSamlSecurityContainer.changeSamlIssuer(data.samlIssuer);
adminSamlSecurityContainer.changeSamlCert(data.samlCert);
adminSamlSecurityContainer.changeSamlAttrMapId(data.samlAttrMapId);
adminSamlSecurityContainer.changeSamlAttrMapUserName(data.samlAttrMapUsername);
adminSamlSecurityContainer.changeSamlAttrMapMail(data.samlAttrMapMail);
adminSamlSecurityContainer.changeSamlAttrMapFirstName(data.samlAttrMapFirstName);
adminSamlSecurityContainer.changeSamlAttrMapLastName(data.samlAttrMapLastName);
adminSamlSecurityContainer.changeSamlABLCRule(data.samlABLCRule);

try {
await adminSamlSecurityContainer.updateSamlSetting();
await adminSamlSecurityContainer.updateSamlSetting({
samlEntryPoint: data.samlEntryPoint,
samlIssuer: data.samlIssuer,
samlCert: data.samlCert,
samlAttrMapId: data.samlAttrMapId,
samlAttrMapUsername: data.samlAttrMapUsername,
samlAttrMapMail: data.samlAttrMapMail,
samlAttrMapFirstName: data.samlAttrMapFirstName,
samlAttrMapLastName: data.samlAttrMapLastName,
isSameUsernameTreatedAsIdenticalUser: adminSamlSecurityContainer.state.isSameUsernameTreatedAsIdenticalUser,
isSameEmailTreatedAsIdenticalUser: adminSamlSecurityContainer.state.isSameEmailTreatedAsIdenticalUser,
samlABLCRule: data.samlABLCRule,
});
toastSuccess(t('security_settings.SAML.updated_saml'));
}
catch (err) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,21 @@ const SecuritySettingComponent: React.FC<Props> = ({ adminGeneralSecurityContain

const onSubmit = useCallback(async(data: FormData) => {
try {
// Update sessionMaxAge from form data
await adminGeneralSecurityContainer.setSessionMaxAge(data.sessionMaxAge);
// Save all security settings
await adminGeneralSecurityContainer.updateGeneralSecuritySetting();
// Save all security settings with form data
await adminGeneralSecurityContainer.updateGeneralSecuritySetting({
sessionMaxAge: data.sessionMaxAge,
restrictGuestMode: adminGeneralSecurityContainer.state.currentRestrictGuestMode,
pageDeletionAuthority: adminGeneralSecurityContainer.state.currentPageDeletionAuthority,
pageCompleteDeletionAuthority: adminGeneralSecurityContainer.state.currentPageCompleteDeletionAuthority,
pageRecursiveDeletionAuthority: adminGeneralSecurityContainer.state.currentPageRecursiveDeletionAuthority,
pageRecursiveCompleteDeletionAuthority: adminGeneralSecurityContainer.state.currentPageRecursiveCompleteDeletionAuthority,
isAllGroupMembershipRequiredForPageCompleteDeletion: adminGeneralSecurityContainer.state.isAllGroupMembershipRequiredForPageCompleteDeletion,
hideRestrictedByGroup: adminGeneralSecurityContainer.state.currentGroupRestrictionDisplayMode === 'Hidden',
hideRestrictedByOwner: adminGeneralSecurityContainer.state.currentOwnerRestrictionDisplayMode === 'Hidden',
isUsersHomepageDeletionEnabled: adminGeneralSecurityContainer.state.isUsersHomepageDeletionEnabled,
isForceDeleteUserHomepageOnUserDeletion: adminGeneralSecurityContainer.state.isForceDeleteUserHomepageOnUserDeletion,
isRomUserAllowedToComment: adminGeneralSecurityContainer.state.isRomUserAllowedToComment,
});
toastSuccess(t('security_settings.updated_general_security_setting'));
}
catch (err) {
Expand Down
19 changes: 16 additions & 3 deletions apps/app/src/client/services/AdminGeneralSecurityContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -239,9 +239,22 @@ export default class AdminGeneralSecurityContainer extends Container {
* @memberOf AdminGeneralSecuritySContainer
* @return {string} Appearance
*/
async updateGeneralSecuritySetting() {

let requestParams = {
async updateGeneralSecuritySetting(formData) {

let requestParams = formData != null ? {
sessionMaxAge: formData.sessionMaxAge,
restrictGuestMode: formData.restrictGuestMode,
pageDeletionAuthority: formData.pageDeletionAuthority,
pageCompleteDeletionAuthority: formData.pageCompleteDeletionAuthority,
pageRecursiveDeletionAuthority: formData.pageRecursiveDeletionAuthority,
pageRecursiveCompleteDeletionAuthority: formData.pageRecursiveCompleteDeletionAuthority,
isAllGroupMembershipRequiredForPageCompleteDeletion: formData.isAllGroupMembershipRequiredForPageCompleteDeletion,
hideRestrictedByGroup: formData.hideRestrictedByGroup,
hideRestrictedByOwner: formData.hideRestrictedByOwner,
isUsersHomepageDeletionEnabled: formData.isUsersHomepageDeletionEnabled,
isForceDeleteUserHomepageOnUserDeletion: formData.isForceDeleteUserHomepageOnUserDeletion,
isRomUserAllowedToComment: formData.isRomUserAllowedToComment,
} : {
sessionMaxAge: this.state.sessionMaxAge,
restrictGuestMode: this.state.currentRestrictGuestMode,
pageDeletionAuthority: this.state.currentPageDeletionAuthority,
Expand Down
28 changes: 10 additions & 18 deletions apps/app/src/client/services/AdminGitHubSecurityContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,20 +61,6 @@ export default class AdminGitHubSecurityContainer extends Container {
return 'AdminGitHubSecurityContainer';
}

/**
* Change githubClientId
*/
changeGitHubClientId(value) {
this.setState({ githubClientId: value });
}

/**
* Change githubClientSecret
*/
changeGitHubClientSecret(value) {
this.setState({ githubClientSecret: value });
}

/**
* Switch isSameUsernameTreatedAsIdenticalUser
*/
Expand All @@ -85,10 +71,16 @@ export default class AdminGitHubSecurityContainer extends Container {
/**
* Update githubSetting
*/
async updateGitHubSetting() {
const { githubClientId, githubClientSecret, isSameUsernameTreatedAsIdenticalUser } = this.state;

let requestParams = { githubClientId, githubClientSecret, isSameUsernameTreatedAsIdenticalUser };
async updateGitHubSetting(formData) {
let requestParams = formData != null ? {
githubClientId: formData.githubClientId,
githubClientSecret: formData.githubClientSecret,
isSameUsernameTreatedAsIdenticalUser: formData.isSameUsernameTreatedAsIdenticalUser,
} : {
githubClientId: this.state.githubClientId,
githubClientSecret: this.state.githubClientSecret,
isSameUsernameTreatedAsIdenticalUser: this.state.isSameUsernameTreatedAsIdenticalUser,
};

requestParams = await removeNullPropertyFromObject(requestParams);
const response = await apiv3Put('/security-setting/github-oauth', requestParams);
Expand Down
28 changes: 9 additions & 19 deletions apps/app/src/client/services/AdminGoogleSecurityContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,20 +62,6 @@ export default class AdminGoogleSecurityContainer extends Container {
return 'AdminGoogleSecurityContainer';
}

/**
* Change googleClientId
*/
changeGoogleClientId(value) {
this.setState({ googleClientId: value });
}

/**
* Change googleClientSecret
*/
changeGoogleClientSecret(value) {
this.setState({ googleClientSecret: value });
}

/**
* Switch isSameEmailTreatedAsIdenticalUser
*/
Expand All @@ -87,11 +73,15 @@ export default class AdminGoogleSecurityContainer extends Container {
/**
* Update googleSetting
*/
async updateGoogleSetting() {
const { googleClientId, googleClientSecret, isSameEmailTreatedAsIdenticalUser } = this.state;

let requestParams = {
googleClientId, googleClientSecret, isSameEmailTreatedAsIdenticalUser,
async updateGoogleSetting(formData) {
let requestParams = formData != null ? {
googleClientId: formData.googleClientId,
googleClientSecret: formData.googleClientSecret,
isSameEmailTreatedAsIdenticalUser: formData.isSameEmailTreatedAsIdenticalUser,
} : {
googleClientId: this.state.googleClientId,
googleClientSecret: this.state.googleClientSecret,
isSameEmailTreatedAsIdenticalUser: this.state.isSameEmailTreatedAsIdenticalUser,
};

requestParams = await removeNullPropertyFromObject(requestParams);
Expand Down
Loading
Loading