diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 5f58f20cdd..cbf40d445a 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.10.0 +current_version = 1.13.0 commit = False tag = False diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 2e773803f3..0e9e63495e 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -106,3 +106,4 @@ jobs: JAVA_FILE_NAME: checkstyle.xml VALIDATE_SHELL_SHFMT: false VALIDATE_DOCKERFILE_HADOLINT: false + VALIDATE_JSCPD: false diff --git a/.gitpod.yml b/.gitpod.yml index 38a4dc3908..4f730bbcd3 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -5,7 +5,7 @@ image: tasks: - name: install dependency init: | - npm install -g pnpm + npm install -g pnpm@8.13.1 make install command: | echo "Happy Coding" diff --git a/.version b/.version index 81c871de46..feaae22bac 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -1.10.0 +1.13.0 diff --git a/.vscode/settings.json b/.vscode/settings.json index 6ff325f6e3..c13dd11bb9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,7 @@ { "typescript.tsdk": ".yarn/sdks/typescript/lib", "editor.codeActionsOnSave": { - "source.fixAll.eslint": true + "source.fixAll.eslint": "explicit" }, "editor.formatOnSave": false, "[typescriptreact]": { diff --git a/CHANGELOG.md b/CHANGELOG.md index 305a206c73..0c378559d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # APITable CHANGELOG +## [v1.10.0-beta.3](https://github.com/apitable/apitable/releases/tag/v1.10.0-beta.3) (2024-05-06) + + +### What's more + +* sync: hosted cloud ([#1734](https://github.com/apitable/apitable/pull/1734)) @zoe-icu +* refactor: add changelog ([#1721](https://github.com/apitable/apitable/pull/1721)) @zoe-icu +* chore: upgrade mysql2 ([#11084](https://github.com/apitable/apitable/pull/1734/commits/d299e369b8047f577e915adfe3b627317af3028c)) @zoe-icu +## [v1.10.0-beta.2](https://github.com/apitable/apitable/releases/tag/v1.10.0-beta.2) (2024-04-17) + + +### Bug fixes + +* fix: delete automation trigger ([#1657](https://github.com/apitable/apitable/pull/1657)) @zoe-icu + +### What's more + +* sync: hosted cloud ([#1720](https://github.com/apitable/apitable/pull/1720)) @zoe-icu ## [v1.10.0-beta](https://github.com/apitable/apitable/releases/tag/v1.10.0-beta) (2024-03-11) diff --git a/backend-server/.version b/backend-server/.version index 81c871de46..feaae22bac 100644 --- a/backend-server/.version +++ b/backend-server/.version @@ -1 +1 @@ -1.10.0 +1.13.0 diff --git a/backend-server/application/build.gradle b/backend-server/application/build.gradle index f173612219..2aac752095 100644 --- a/backend-server/application/build.gradle +++ b/backend-server/application/build.gradle @@ -33,6 +33,7 @@ dependencies { implementation libs.spring.boot.thymeleaf implementation libs.spring.integration.redis implementation libs.spring.session.redis + implementation libs.spring.boot.oauth2.client implementation libs.mysql runtimeOnly libs.mysql implementation libs.spring.security.cas diff --git a/backend-server/application/src/main/java/com/apitable/automation/service/IAutomationTriggerService.java b/backend-server/application/src/main/java/com/apitable/automation/service/IAutomationTriggerService.java index 650f44fcda..e5d8d6f341 100644 --- a/backend-server/application/src/main/java/com/apitable/automation/service/IAutomationTriggerService.java +++ b/backend-server/application/src/main/java/com/apitable/automation/service/IAutomationTriggerService.java @@ -70,15 +70,6 @@ public interface IAutomationTriggerService { */ List update(Long userId, String triggerId, String spaceId, UpdateTriggerRO data); - /** - * Delete trigger. - * - * @param robotId robot id - * @param triggerId trigger id - * @param userId operator user id - */ - void deleteByDatabus(String robotId, String triggerId, Long userId); - /** * Delete trigger. diff --git a/backend-server/application/src/main/java/com/apitable/automation/service/impl/AutomationTriggerServiceImpl.java b/backend-server/application/src/main/java/com/apitable/automation/service/impl/AutomationTriggerServiceImpl.java index 7898a86b9c..d6601717d5 100644 --- a/backend-server/application/src/main/java/com/apitable/automation/service/impl/AutomationTriggerServiceImpl.java +++ b/backend-server/application/src/main/java/com/apitable/automation/service/impl/AutomationTriggerServiceImpl.java @@ -18,7 +18,6 @@ package com.apitable.automation.service.impl; -import static com.apitable.automation.enums.AutomationException.AUTOMATION_ROBOT_NOT_EXIST; import static com.apitable.automation.enums.AutomationException.AUTOMATION_TRIGGER_LIMIT; import static com.apitable.automation.enums.AutomationException.AUTOMATION_TRIGGER_NOT_EXIST; import static com.apitable.automation.model.TriggerSimpleVO.triggerComparator; @@ -29,7 +28,6 @@ import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONUtil; -import com.apitable.automation.entity.AutomationRobotEntity; import com.apitable.automation.entity.AutomationTriggerEntity; import com.apitable.automation.enums.AutomationTriggerType; import com.apitable.automation.mapper.AutomationTriggerMapper; @@ -47,8 +45,6 @@ import com.apitable.shared.config.properties.LimitProperties; import com.apitable.shared.util.IdUtil; import com.apitable.starter.databus.client.api.AutomationDaoApiApi; -import com.apitable.starter.databus.client.model.ApiResponseAutomationTriggerSO; -import com.apitable.starter.databus.client.model.AutomationRobotTriggerRO; import com.apitable.starter.databus.client.model.AutomationTriggerSO; import com.baomidou.mybatisplus.core.toolkit.IdWorker; import jakarta.annotation.Resource; @@ -61,7 +57,6 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.client.RestClientException; /** * automation trigger service impl. @@ -133,19 +128,11 @@ public List update(Long userId, String triggerId, String spaceId, ExceptionUtil.isNotNull(trigger, AUTOMATION_TRIGGER_NOT_EXIST); String scheduleTriggerTypeId = iAutomationTriggerTypeService.getTriggerTypeByEndpoint( AutomationTriggerType.SCHEDULED_TIME_ARRIVE.getType()); - if (StrUtil.isNotBlank(data.getTriggerTypeId())) { - // change trigger type to schedule should create schedule - if (!trigger.getTriggerId().equals(scheduleTriggerTypeId) - && data.getTriggerTypeId().equals(scheduleTriggerTypeId)) { - automationServiceFacade.createSchedule(spaceId, triggerId, - JSONUtil.toJsonStr(JSONUtil.createObj())); - } - // change schedule to another type - if (trigger.getTriggerId().equals(scheduleTriggerTypeId) - && !data.getTriggerTypeId().equals(scheduleTriggerTypeId)) { - automationServiceFacade.updateSchedule(triggerId, - JSONUtil.toJsonStr(JSONUtil.createObj())); - } + if (StrUtil.isNotBlank(data.getTriggerTypeId()) + && !trigger.getTriggerTypeId().equals(data.getTriggerTypeId())) { + // change trigger type should reset schedule config to empty object {} + automationServiceFacade.updateSchedule(triggerId, + JSONUtil.toJsonStr(JSONUtil.createObj())); trigger.setTriggerTypeId(data.getTriggerTypeId()); } if (StrUtil.isNotBlank(data.getPrevTriggerId())) { @@ -166,22 +153,6 @@ public List update(Long userId, String triggerId, String spaceId, return formatVoFromEntities(ListUtil.of(trigger)); } - @Override - public void deleteByDatabus(String robotId, String triggerId, Long userId) { - AutomationRobotTriggerRO ro = new AutomationRobotTriggerRO(); - ro.setUserId(userId); - ro.setIsDeleted(true); - ro.setTriggerId(triggerId); - try { - ApiResponseAutomationTriggerSO response = - automationDaoApiApi.daoCreateOrUpdateAutomationRobotTrigger(robotId, ro); - ExceptionUtil.isFalse( - AUTOMATION_ROBOT_NOT_EXIST.getCode().equals(response.getCode()), - AUTOMATION_ROBOT_NOT_EXIST); - } catch (RestClientException e) { - log.error("Delete trigger: {}", triggerId, e); - } - } @Override @Transactional(rollbackFor = Exception.class) diff --git a/backend-server/application/src/main/java/com/apitable/control/infrastructure/ControlTemplate.java b/backend-server/application/src/main/java/com/apitable/control/infrastructure/ControlTemplate.java index aad920df3f..0d93155118 100644 --- a/backend-server/application/src/main/java/com/apitable/control/infrastructure/ControlTemplate.java +++ b/backend-server/application/src/main/java/com/apitable/control/infrastructure/ControlTemplate.java @@ -259,6 +259,10 @@ protected ControlRoleDict doExecute(Principal principal, ControlId controlId, List unitIds = iUnitService.getUnitIdsByRefIds(CollUtil.newArrayList(principal.getPrincipal())); return doExecute(unitIds, controlId, requestWrapper); + } else if (principal.getPrincipalType() == PrincipalType.TAG_ID) { + List unitIds = + iUnitService.getUnitIdsByRefIds(CollUtil.newArrayList(principal.getPrincipal())); + return doExecute(unitIds, controlId, requestWrapper); } else { throw new UnknownPrincipalTypeException(principal.getPrincipalType()); } diff --git a/backend-server/application/src/main/java/com/apitable/control/infrastructure/PrincipalType.java b/backend-server/application/src/main/java/com/apitable/control/infrastructure/PrincipalType.java index dd53117763..d5c08cdf7e 100644 --- a/backend-server/application/src/main/java/com/apitable/control/infrastructure/PrincipalType.java +++ b/backend-server/application/src/main/java/com/apitable/control/infrastructure/PrincipalType.java @@ -28,7 +28,8 @@ public enum PrincipalType { UNIT_ID(0), MEMBER_ID(1), TEAM_ID(2), - ROLE_ID(3); + ROLE_ID(3), + TAG_ID(4); private final int val; diff --git a/backend-server/application/src/main/java/com/apitable/interfaces/social/facade/DefaultSocialServiceFacade.java b/backend-server/application/src/main/java/com/apitable/interfaces/social/facade/DefaultSocialServiceFacade.java index 49a39ca461..556787e7ba 100644 --- a/backend-server/application/src/main/java/com/apitable/interfaces/social/facade/DefaultSocialServiceFacade.java +++ b/backend-server/application/src/main/java/com/apitable/interfaces/social/facade/DefaultSocialServiceFacade.java @@ -102,4 +102,15 @@ public List fuzzySearchIfSatisfyCondition(String spaceId, String word) { public void eventCall(T event) { } + + /** + * get union id map. + * + * @param userIds user ids + * @return map + */ + @Override + public Map getUnionIdMap(List userIds) { + return Collections.emptyMap(); + } } diff --git a/backend-server/application/src/main/java/com/apitable/interfaces/social/facade/SocialServiceFacade.java b/backend-server/application/src/main/java/com/apitable/interfaces/social/facade/SocialServiceFacade.java index 71a1912f30..e945993291 100644 --- a/backend-server/application/src/main/java/com/apitable/interfaces/social/facade/SocialServiceFacade.java +++ b/backend-server/application/src/main/java/com/apitable/interfaces/social/facade/SocialServiceFacade.java @@ -58,4 +58,12 @@ void checkWhetherSpaceCanChangeMainAdmin(String spaceId, Long opMemberId, Long a List fuzzySearchIfSatisfyCondition(String spaceId, String word); void eventCall(T event); + + /** + * get union id map. + * + * @param userIds user ids + * @return map + */ + Map getUnionIdMap(List userIds); } diff --git a/backend-server/application/src/main/java/com/apitable/internal/controller/InternalOrganizationController.java b/backend-server/application/src/main/java/com/apitable/internal/controller/InternalOrganizationController.java index 693ca5bb8e..4d21baea01 100644 --- a/backend-server/application/src/main/java/com/apitable/internal/controller/InternalOrganizationController.java +++ b/backend-server/application/src/main/java/com/apitable/internal/controller/InternalOrganizationController.java @@ -37,6 +37,7 @@ import jakarta.annotation.Resource; import jakarta.validation.Valid; import java.util.List; +import java.util.Objects; import org.springframework.web.bind.annotation.RestController; /** @@ -72,7 +73,9 @@ public class InternalOrganizationController { @Parameter(name = "all", description = "whether to load all departments and members", schema = @Schema(type = "boolean"), in = ParameterIn.QUERY), @Parameter(name = "searchEmail", description = "whether to search for emails", - schema = @Schema(type = "boolean"), in = ParameterIn.QUERY) + schema = @Schema(type = "boolean"), in = ParameterIn.QUERY), + @Parameter(name = "type", description = "the return unit type, 1-department, 3-member", schema = @Schema(type = "integer"), + in = ParameterIn.QUERY, example = "3") }) public ResponseData> loadOrSearch(@Valid LoadSearchDTO params) { // sharing node/template: un login users invoke processing @@ -81,6 +84,11 @@ public ResponseData> loadOrSearch(@Valid LoadSearchDTO params) List vos = iOrganizationService.loadOrSearchInfo(userId, spaceId, params, null); List existUnitInfo = vos.stream().filter(unitInfoVo -> !unitInfoVo.getIsDeleted()).collect(toList()); + if (null != params.getType()) { + existUnitInfo = existUnitInfo.stream() + .filter(unitInfoVo -> Objects.equals(unitInfoVo.getType(), params.getType())) + .collect(toList()); + } return ResponseData.success(existUnitInfo); } } diff --git a/backend-server/application/src/main/java/com/apitable/internal/service/impl/InternalSpaceServiceImpl.java b/backend-server/application/src/main/java/com/apitable/internal/service/impl/InternalSpaceServiceImpl.java index 06aeb94e67..8c88632e1a 100644 --- a/backend-server/application/src/main/java/com/apitable/internal/service/impl/InternalSpaceServiceImpl.java +++ b/backend-server/application/src/main/java/com/apitable/internal/service/impl/InternalSpaceServiceImpl.java @@ -145,10 +145,9 @@ public InternalSpaceApiUsageVo getSpaceEntitlementApiUsageVo(String spaceId) { InternalSpaceApiUsageVo vo = assembler.toApiUsageVo(planFeature); LocalDate now = ClockManager.me().getLocalDateNow(); CycleDateRange dateRange = SubscriptionDateRange.calculateCycleDate(subscriptionInfo, now); - vo.setApiUsageUsedCount( - iStaticsService.getCurrentMonthApiUsage(spaceId, dateRange.getCycleEndDate())); - vo.setApiCallUsedNumsCurrentMonth( - iStaticsService.getCurrentMonthApiUsage(spaceId, dateRange.getCycleEndDate())); + Long count = iStaticsService.getCurrentMonthApiUsage(spaceId, dateRange.getCycleEndDate()); + vo.setApiUsageUsedCount(count); + vo.setApiCallUsedNumsCurrentMonth(count); vo.setIsAllowOverLimit(true); return vo; } diff --git a/backend-server/application/src/main/java/com/apitable/organization/controller/MemberController.java b/backend-server/application/src/main/java/com/apitable/organization/controller/MemberController.java index 770026e5ff..33bc4e8850 100644 --- a/backend-server/application/src/main/java/com/apitable/organization/controller/MemberController.java +++ b/backend-server/application/src/main/java/com/apitable/organization/controller/MemberController.java @@ -74,6 +74,7 @@ import com.apitable.shared.context.LoginContext; import com.apitable.shared.context.SessionContext; import com.apitable.shared.holder.SpaceHolder; +import com.apitable.shared.security.IFrequencyLimitService; import com.apitable.shared.util.page.PageHelper; import com.apitable.shared.util.page.PageInfo; import com.apitable.shared.util.page.PageObjectParam; @@ -138,6 +139,9 @@ public class MemberController { @Resource private BlackListServiceFacade blackListServiceFacade; + @Resource + private IFrequencyLimitService iFrequencyLimitService; + @Resource private IRoleService iRoleService; @@ -507,12 +511,13 @@ public void downloadTemplate(HttpServletResponse response) { @Parameter(name = ParamsConstants.SPACE_ID, description = "space id", required = true, schema = @Schema(type = "string"), in = ParameterIn.HEADER, example = "spcyQkKp9XJEl") public ResponseData uploadExcel(UploadMemberTemplateRo data) { - // human verification - humanVerificationServiceFacade.verifyNonRobot(new NonRobotMetadata(data.getData())); String spaceId = LoginContext.me().getSpaceId(); // check black space blackListServiceFacade.checkSpace(spaceId); + iFrequencyLimitService.spaceInviteFrequency(spaceId); iSpaceService.checkCanOperateSpaceUpdate(spaceId); + // human verification + humanVerificationServiceFacade.verifyNonRobot(new NonRobotMetadata(data.getData())); SubscriptionInfo subscriptionInfo = entitlementServiceFacade.getSpaceSubscription(spaceId); if (subscriptionInfo.isFree() && iMemberService.shouldPreventInvitation(spaceId)) { diff --git a/backend-server/application/src/main/java/com/apitable/organization/controller/OrganizationController.java b/backend-server/application/src/main/java/com/apitable/organization/controller/OrganizationController.java index 27f81922e2..0a0a095010 100644 --- a/backend-server/application/src/main/java/com/apitable/organization/controller/OrganizationController.java +++ b/backend-server/application/src/main/java/com/apitable/organization/controller/OrganizationController.java @@ -67,6 +67,7 @@ import jakarta.validation.Valid; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; @@ -289,7 +290,9 @@ public ResponseData getSubUnitList( @Parameter(name = "all", description = "whether to load all departments and members", schema = @Schema(type = "boolean"), in = ParameterIn.QUERY), @Parameter(name = "searchEmail", description = "whether to search for emails", - schema = @Schema(type = "boolean"), in = ParameterIn.QUERY) + schema = @Schema(type = "boolean"), in = ParameterIn.QUERY), + @Parameter(name = "type", description = "the return unit type, 1-department, 3-member", schema = @Schema(type = "integer"), + in = ParameterIn.QUERY, example = "3") }) public ResponseData> loadOrSearch(@Valid LoadSearchDTO params) { // sharing node/template: un login users invoke processing @@ -321,6 +324,11 @@ public ResponseData> loadOrSearch(@Valid LoadSearchDTO params) iOrganizationService.loadOrSearchInfo(userId, spaceId, params, sharer); List existUnitInfo = vos.stream().filter(unitInfoVo -> !unitInfoVo.getIsDeleted()).collect(toList()); + if (null != params.getType()) { + existUnitInfo = existUnitInfo.stream() + .filter(unitInfoVo -> Objects.equals(unitInfoVo.getType(), params.getType())) + .collect(toList()); + } return ResponseData.success(existUnitInfo); } diff --git a/backend-server/application/src/main/java/com/apitable/organization/dto/LoadSearchDTO.java b/backend-server/application/src/main/java/com/apitable/organization/dto/LoadSearchDTO.java index 1971bd792f..f2495c29d0 100644 --- a/backend-server/application/src/main/java/com/apitable/organization/dto/LoadSearchDTO.java +++ b/backend-server/application/src/main/java/com/apitable/organization/dto/LoadSearchDTO.java @@ -45,4 +45,6 @@ public class LoadSearchDTO { private Boolean searchEmail; private String userId; + + private Integer type; } diff --git a/backend-server/application/src/main/java/com/apitable/organization/dto/TagInfoDto.java b/backend-server/application/src/main/java/com/apitable/organization/dto/TagInfoDto.java new file mode 100644 index 0000000000..926e111cee --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/organization/dto/TagInfoDto.java @@ -0,0 +1,17 @@ +package com.apitable.organization.dto; + +import lombok.Data; + +/** + * tag info dto. + */ +@Data +public class TagInfoDto { + + private Long id; + + private Long unitId; + + private String tagName; + +} diff --git a/backend-server/application/src/main/java/com/apitable/organization/entity/TagEntity.java b/backend-server/application/src/main/java/com/apitable/organization/entity/TagEntity.java new file mode 100644 index 0000000000..48e38dda74 --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/organization/entity/TagEntity.java @@ -0,0 +1,71 @@ +package com.apitable.organization.entity; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import java.io.Serializable; +import java.time.LocalDateTime; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + *

+ * Tag Structure - unit tag Table. + *

+ * + * @author Mybatis Generator Tool + */ +@Data +@Builder(toBuilder = true) +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@EqualsAndHashCode +@TableName(keepGlobalPrefix = true, value = "unit_tag") +public class TagEntity implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * Primary Key. + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private Long id; + + + /** + * Tag Group ID(link#xxxx_org_tag_group#id). + */ + private String groupId; + + /** + * Space ID(link#xxxx_space#space_id). + */ + private String spaceId; + + /** + * Tag Name. + */ + private String tagName; + + /** + * Sort in space (default starts from 1). + */ + private int sequence; + + /** + * Create Time. + */ + private LocalDateTime createdAt; + + /** + * Update Time. + */ + private LocalDateTime updatedAt; +} diff --git a/backend-server/application/src/main/java/com/apitable/organization/entity/TagMemberRelEntity.java b/backend-server/application/src/main/java/com/apitable/organization/entity/TagMemberRelEntity.java new file mode 100644 index 0000000000..5f696589ae --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/organization/entity/TagMemberRelEntity.java @@ -0,0 +1,62 @@ +package com.apitable.organization.entity; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import java.io.Serializable; +import java.time.LocalDateTime; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + * Tag member rel entity. + */ +@Data +@Builder(toBuilder = true) +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@EqualsAndHashCode +@TableName(keepGlobalPrefix = true, value = "unit_tag_member_rel") +public class TagMemberRelEntity implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * Primary Key. + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private Long id; + + + /** + * Tag Group ID(link#xxxx_org_tag_group#id). + */ + private Long tagId; + + /** + * Member ID. + */ + private Long memberId; + + /** + * the creator member id. + */ + @TableField(fill = FieldFill.INSERT) + private Long creator; + + /** + * Create Time. + */ + private LocalDateTime createdAt; + + /** + * Update Time. + */ + private LocalDateTime updatedAt; +} diff --git a/backend-server/application/src/main/java/com/apitable/organization/enums/UnitType.java b/backend-server/application/src/main/java/com/apitable/organization/enums/UnitType.java index a1ec2e7574..ca31732184 100644 --- a/backend-server/application/src/main/java/com/apitable/organization/enums/UnitType.java +++ b/backend-server/application/src/main/java/com/apitable/organization/enums/UnitType.java @@ -37,7 +37,9 @@ public enum UnitType { ROLE(2), - MEMBER(3); + MEMBER(3), + + TAG(4); private final Integer type; diff --git a/backend-server/application/src/main/java/com/apitable/organization/mapper/TagMapper.java b/backend-server/application/src/main/java/com/apitable/organization/mapper/TagMapper.java new file mode 100644 index 0000000000..258ca28ad8 --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/organization/mapper/TagMapper.java @@ -0,0 +1,34 @@ +package com.apitable.organization.mapper; + +import com.apitable.organization.dto.TagInfoDto; +import com.apitable.organization.entity.TagEntity; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import java.util.List; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +/** + * tag mapper. + */ +@Mapper +public interface TagMapper extends BaseMapper { + + /** + * Update tag name. + * + * @param tagId tag Id + * @param name tag name + */ + void updateName(@Param("tagId") Long tagId, @Param("name") String name); + + + /** + * query tag info by ids and space id. + * + * @param ids the rows' id + * @param spaceId the space's id + * @return tag info + */ + List selectTagInfoDtoByIdsAndSpaceId(@Param("ids") List ids, + @Param("spaceId") String spaceId); +} diff --git a/backend-server/application/src/main/java/com/apitable/organization/mapper/TagMemberRelMapper.java b/backend-server/application/src/main/java/com/apitable/organization/mapper/TagMemberRelMapper.java new file mode 100644 index 0000000000..399cc5dbea --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/organization/mapper/TagMemberRelMapper.java @@ -0,0 +1,74 @@ +package com.apitable.organization.mapper; + +import com.apitable.organization.entity.TagMemberRelEntity; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import java.util.Collection; +import java.util.List; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +/** + * tag group member rel mapper. + */ +@Mapper +public interface TagMemberRelMapper extends BaseMapper { + + /** + * insert batch. + * + * @param entities tag-group-member-ref + * @return affected rows + */ + int insertBatch(@Param("entities") List entities); + + /** + * query tag members by tagIds. + * + * @param tagIds tag ids + * @return tag-group-member-ref + */ + List selectByTagIds(@Param("tagIds") List tagIds); + + /** + * query member-ids by tagId. + * + * @param tagIds tag ids + * @return member ids + */ + List selectMemberIdsByTag(@Param("tagIds") List tagIds); + + /** + * delete by member id. + * + * @param memberIds member ids + * @return affected rows + */ + int deleteByMemberIdAndTagId(@Param("tagId") Long tagId, + @Param("memberIds") List memberIds); + + /** + * delete by tag id. + * + * @param tagId tag id + * @return affected rows + */ + int deleteMembersByTagId(@Param("tagId") Long tagId); + + /** + * delete bulk with tag group ids. + * + * @param tagIds tag ids + * @return affected rows + */ + int deleteByTagIds(@Param("tagIds") Collection tagIds); + + + /** + * get tag' id by role member id. + * + * @param memberId the tag member's id + * @return the tag' id of the member's ref. + */ + List selectTagIdsByMemberId(@Param("memberId") Long memberId); + +} diff --git a/backend-server/application/src/main/java/com/apitable/organization/mapper/TeamMemberRelMapper.java b/backend-server/application/src/main/java/com/apitable/organization/mapper/TeamMemberRelMapper.java index 6b8e0a9424..fee09008b6 100644 --- a/backend-server/application/src/main/java/com/apitable/organization/mapper/TeamMemberRelMapper.java +++ b/backend-server/application/src/main/java/com/apitable/organization/mapper/TeamMemberRelMapper.java @@ -53,6 +53,7 @@ public interface TeamMemberRelMapper extends ExpandBaseMapper teamIds); /** diff --git a/backend-server/application/src/main/java/com/apitable/organization/service/ITagMemberRelService.java b/backend-server/application/src/main/java/com/apitable/organization/service/ITagMemberRelService.java new file mode 100644 index 0000000000..d390cfd3a3 --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/organization/service/ITagMemberRelService.java @@ -0,0 +1,22 @@ +package com.apitable.organization.service; + +import com.apitable.organization.entity.TagMemberRelEntity; +import java.util.List; + +/** + * Tag Member rel. + */ +public interface ITagMemberRelService { + + List getByTagIds(List tagIds); + + List getMemberIdByTagIds(List tagIds); + + void removeMembers(Long tagId, List memberIds); + + void addMembers(Long tagId, List memberIds); + + void deleteMembersByTagId(Long tagId); + + List getTagIdsByTagMemberId(Long memberId); +} diff --git a/backend-server/application/src/main/java/com/apitable/organization/service/ITagService.java b/backend-server/application/src/main/java/com/apitable/organization/service/ITagService.java new file mode 100644 index 0000000000..e7076f1fb4 --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/organization/service/ITagService.java @@ -0,0 +1,47 @@ +package com.apitable.organization.service; + +import com.apitable.organization.vo.TagInfoVo; +import java.util.List; + +/** + * Tag service. + */ +public interface ITagService { + + /** + * create tag. + * + * @param groupId tag groupId , options , default 0 + * @param spaceId space Id + * @param tagName tagName + * @return tagId + */ + Long createTag(Long groupId, String spaceId, String tagName); + + /** + * update tag name. + * + * @param tagId tag id + * @param tagName tag name + */ + void updateTagName(Long tagId, String tagName); + + /** + * delete tag. + * + * @param tagId tag id + */ + void delete(Long tagId); + + + List getTagVos(String spaceId, List tagIds); + + + /** + * get members' id in tag. + * + * @param tagIds the tag' id + * @return the members' id + */ + List getMemberIdsByTagIds(List tagIds); +} diff --git a/backend-server/application/src/main/java/com/apitable/organization/service/impl/MemberServiceImpl.java b/backend-server/application/src/main/java/com/apitable/organization/service/impl/MemberServiceImpl.java index 2edbc8720f..2eaab5eab0 100644 --- a/backend-server/application/src/main/java/com/apitable/organization/service/impl/MemberServiceImpl.java +++ b/backend-server/application/src/main/java/com/apitable/organization/service/impl/MemberServiceImpl.java @@ -73,6 +73,7 @@ import com.apitable.organization.service.IMemberService; import com.apitable.organization.service.IRoleMemberService; import com.apitable.organization.service.IRoleService; +import com.apitable.organization.service.ITagMemberRelService; import com.apitable.organization.service.ITeamMemberRelService; import com.apitable.organization.service.ITeamService; import com.apitable.organization.service.IUnitService; @@ -91,6 +92,7 @@ import com.apitable.shared.component.notification.NotifyMailFactory; import com.apitable.shared.component.notification.NotifyMailFactory.MailWithLang; import com.apitable.shared.config.properties.ConstProperties; +import com.apitable.shared.config.properties.LimitProperties; import com.apitable.shared.constants.MailPropConstants; import com.apitable.shared.context.LoginContext; import com.apitable.shared.context.SessionContext; @@ -159,6 +161,9 @@ public class MemberServiceImpl extends ExpandServiceImpl getUnitsByMember(Long memberId) { refRoles.stream().map(RoleMemberDTO::getRoleId).forEach(unitRefIds::add); } List roleIds = iRoleMemberService.getRoleIdsByRoleMemberId(memberId); + List tagIds = iTagMemberRelService.getTagIdsByTagMemberId(memberId); unitRefIds.addAll(roleIds); + unitRefIds.addAll(tagIds); return iUnitService.getUnitIdsByRefIds(unitRefIds); } @@ -1520,7 +1530,7 @@ public boolean shouldPreventInvitation(String spaceId) { LocalDateTime.now().plusDays(1).withHour(0).withMinute(0).withSecond(0); Integer count = spaceInviteRecordMapper.selectCountBySpaceIdAndBetween(spaceId, startAt, endAt); - return count >= constProperties.getMaxInviteCountForFree(); + return count >= limitProperties.getMaxInviteCountForFree(); } @Override diff --git a/backend-server/application/src/main/java/com/apitable/organization/service/impl/TagMemberRelServiceImpl.java b/backend-server/application/src/main/java/com/apitable/organization/service/impl/TagMemberRelServiceImpl.java new file mode 100644 index 0000000000..98dc0c7493 --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/organization/service/impl/TagMemberRelServiceImpl.java @@ -0,0 +1,68 @@ +package com.apitable.organization.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.apitable.organization.entity.TagMemberRelEntity; +import com.apitable.organization.mapper.TagMemberRelMapper; +import com.apitable.organization.service.ITagMemberRelService; +import com.baomidou.mybatisplus.core.toolkit.IdWorker; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import jakarta.annotation.Resource; +import java.util.List; +import java.util.stream.Collectors; +import org.springframework.stereotype.Service; + +/** + * Tag member rel service. + */ +@Service +public class TagMemberRelServiceImpl extends ServiceImpl + implements ITagMemberRelService { + + @Resource + private TagMemberRelMapper tagGroupMemberRelMapper; + + + @Override + public List getByTagIds(List tagIds) { + return tagGroupMemberRelMapper.selectByTagIds(tagIds); + } + + @Override + public List getMemberIdByTagIds(List tagIds) { + if (CollUtil.isEmpty(tagIds)) { + return CollUtil.newArrayList(); + } + return tagGroupMemberRelMapper.selectMemberIdsByTag(tagIds); + } + + @Override + public void removeMembers(Long tagId, List memberIds) { + if (CollUtil.isEmpty(memberIds)) { + return; + } + tagGroupMemberRelMapper.deleteByMemberIdAndTagId(tagId, memberIds); + } + + @Override + public void addMembers(Long tagId, List memberIds) { + List tagMemberRelEntityList = memberIds.stream().map(m -> { + return TagMemberRelEntity.builder() + .id(IdWorker.getId()) + .tagId(tagId) + .memberId(m) + .build(); + }).collect(Collectors.toList()); + tagGroupMemberRelMapper.insertBatch(tagMemberRelEntityList); + } + + @Override + public void deleteMembersByTagId(Long tagId) { + tagGroupMemberRelMapper.deleteMembersByTagId(tagId); + } + + @Override + public List getTagIdsByTagMemberId(Long memberId) { + return tagGroupMemberRelMapper.selectTagIdsByMemberId(memberId); + } + +} diff --git a/backend-server/application/src/main/java/com/apitable/organization/service/impl/TagServiceImpl.java b/backend-server/application/src/main/java/com/apitable/organization/service/impl/TagServiceImpl.java new file mode 100644 index 0000000000..1bf7f0be3b --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/organization/service/impl/TagServiceImpl.java @@ -0,0 +1,109 @@ +package com.apitable.organization.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.apitable.base.enums.DatabaseException; +import com.apitable.core.util.ExceptionUtil; +import com.apitable.organization.dto.TagInfoDto; +import com.apitable.organization.entity.TagEntity; +import com.apitable.organization.entity.TagMemberRelEntity; +import com.apitable.organization.enums.UnitType; +import com.apitable.organization.mapper.TagMapper; +import com.apitable.organization.mapper.TagMemberRelMapper; +import com.apitable.organization.service.ITagService; +import com.apitable.organization.service.IUnitService; +import com.apitable.organization.vo.TagInfoVo; +import com.baomidou.mybatisplus.core.toolkit.IdWorker; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import jakarta.annotation.Resource; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +/** + * Tag service. + */ +@Slf4j +@Service +public class TagServiceImpl extends ServiceImpl implements ITagService { + + @Resource + IUnitService iUnitService; + + @Resource + private TagMapper tagMapper; + + @Resource + private TagMemberRelMapper tagGroupMemberRelMapper; + + @Override + public Long createTag(Long groupId, String spaceId, String tagName) { + log.info("create tag: {} , spaceId: {}", tagName, spaceId); + TagEntity tagEntity = TagEntity.builder() + .id(IdWorker.getId()) + .spaceId(spaceId) + .tagName(tagName) + .build(); + boolean ret = save(tagEntity); + ExceptionUtil.isTrue(ret, DatabaseException.INSERT_ERROR); + // add ref unit. + iUnitService.create(spaceId, UnitType.TAG, tagEntity.getId()); + return tagEntity.getId(); + } + + @Override + public void updateTagName(Long tagId, String tagName) { + tagMapper.updateName(tagId, tagName); + } + + @Override + public void delete(Long tagId) { + // clear tag's member + tagGroupMemberRelMapper.deleteMembersByTagId(tagId); + // delete the ref unit and control role. + iUnitService.removeByRefId(tagId); + // delete the tag. + boolean ret = removeById(tagId); + ExceptionUtil.isTrue(ret, DatabaseException.DELETE_ERROR); + } + + @Override + public List getTagVos(String spaceId, List tagIds) { + + if (CollUtil.isEmpty(tagIds)) { + return CollUtil.newArrayList(); + } + // query the space's tag. + List tagInfoVoList = new ArrayList<>(); + List tagMemberRelEntityList = + tagGroupMemberRelMapper.selectByTagIds(tagIds); + Map tagCountMap = tagMemberRelEntityList.stream() + .collect(Collectors.groupingBy(TagMemberRelEntity::getTagId, Collectors.counting())); + List tagInfoDtos = tagMapper.selectTagInfoDtoByIdsAndSpaceId(tagIds, spaceId); + //calc member count + tagInfoDtos.stream().forEach(t -> { + Long memberCount = tagCountMap.getOrDefault(t.getId(), 0L); + tagInfoVoList.add(TagInfoVo.builder() + .unitId(t.getUnitId()) + .tagId(t.getId()) + .tagName(t.getTagName()) + .memberCount(memberCount) + .build()); + }); + return tagInfoVoList; + } + + @Override + public List getMemberIdsByTagIds(List tagIds) { + if (CollUtil.isEmpty(tagIds)) { + return CollUtil.newArrayList(); + } + List tagMemberRelEntityList = + tagGroupMemberRelMapper.selectByTagIds(tagIds); + + return tagMemberRelEntityList.stream().map(m -> m.getMemberId()).distinct().collect( + Collectors.toList()); + } +} diff --git a/backend-server/application/src/main/java/com/apitable/organization/service/impl/TeamServiceImpl.java b/backend-server/application/src/main/java/com/apitable/organization/service/impl/TeamServiceImpl.java index 0063d7f46c..ce1801d944 100644 --- a/backend-server/application/src/main/java/com/apitable/organization/service/impl/TeamServiceImpl.java +++ b/backend-server/application/src/main/java/com/apitable/organization/service/impl/TeamServiceImpl.java @@ -264,8 +264,7 @@ public MemberIsolatedInfo checkMemberIsolatedBySpaceId(String spaceId, Long memb public boolean checkHasSubUnitByTeamId(String spaceId, Long teamId) { log.info("Check whether the team has members or teams"); List subTeamIds = baseMapper.selectTeamIdsByParentId(spaceId, teamId); - long subMemberCount = - SqlTool.retCount(teamMemberRelMapper.countByTeamId(Collections.singletonList(teamId))); + long subMemberCount = this.getMemberCount(Collections.singletonList(teamId)); return CollUtil.isNotEmpty(subTeamIds) || subMemberCount > 0; } @@ -273,14 +272,21 @@ public boolean checkHasSubUnitByTeamId(String spaceId, Long teamId) { public long countMemberCountByParentId(Long teamId) { log.info("count the team's members, includes the sub teams' members."); List allSubTeamIds = this.getAllTeamIdsInTeamTree(teamId); - return CollUtil.isNotEmpty(allSubTeamIds) - ? SqlTool.retCount(teamMemberRelMapper.countByTeamId(allSubTeamIds)) : 0; + return this.getMemberCount(allSubTeamIds); } @Override public long getMemberCount(List teamIds) { - // obtain the number of all members in a department - return SqlTool.retCount(teamMemberRelMapper.countByTeamId(teamIds)); + if (CollUtil.isEmpty(teamIds)) { + return 0; + } + // obtain the count of all members in departments + List teamMemberRelEntities = + DBUtil.batchSelectByFieldIn(teamIds, teamMemberRelMapper::selectByTeamIds, 100); + // deduplication + Set memberIds = teamMemberRelEntities.stream() + .map(TeamMemberRelEntity::getMemberId).collect(Collectors.toSet()); + return memberIds.size(); } @Override @@ -504,7 +510,7 @@ public TeamInfoVo getTeamInfoById(Long teamId) { } teamInfo.setTeamId(teamId); List teamIds = this.getAllTeamIdsInTeamTree(teamId); - Long memberCount = SqlHelper.retCount(teamMemberRelMapper.countByTeamId(teamIds)); + Long memberCount = this.getMemberCount(teamIds); teamInfo.setMemberCount(memberCount); return teamInfo; } diff --git a/backend-server/application/src/main/java/com/apitable/organization/vo/TagInfoVo.java b/backend-server/application/src/main/java/com/apitable/organization/vo/TagInfoVo.java new file mode 100644 index 0000000000..ada871d969 --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/organization/vo/TagInfoVo.java @@ -0,0 +1,35 @@ +package com.apitable.organization.vo; + +import com.apitable.shared.support.serializer.NullNumberSerializer; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; +import lombok.Data; + +/** + *

+ * tag's info. + *

+ */ +@Data +@Builder +@Schema(description = "tag's info") +public class TagInfoVo { + + @Schema(description = "unit id", example = "1") + @JsonSerialize(using = ToStringSerializer.class) + private Long unitId; + + @Schema(description = "tag id", example = "1") + @JsonSerialize(using = ToStringSerializer.class) + private Long tagId; + + @Schema(description = "tag name", example = "1") + @JsonSerialize(using = ToStringSerializer.class) + private String tagName; + + @Schema(description = "Number of tag members", example = "3") + @JsonSerialize(nullsUsing = NullNumberSerializer.class) + private Long memberCount; +} diff --git a/backend-server/application/src/main/java/com/apitable/player/config/properties/NotificationProperties.java b/backend-server/application/src/main/java/com/apitable/player/config/properties/NotificationProperties.java new file mode 100644 index 0000000000..404e53e71b --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/player/config/properties/NotificationProperties.java @@ -0,0 +1,31 @@ +/* + * APITable Ltd. + * Copyright (C) 2022 APITable Ltd. + * + * This code file is part of APITable Enterprise Edition. + * + * It is subject to the APITable Commercial License and conditional on having a fully paid-up license from APITable. + * + * Access to this code file or other code files in this `enterprise` directory and its subdirectories does not constitute permission to use this code or APITable Enterprise Edition features. + * + * Unless otherwise noted, all files Copyright © 2022 APITable Ltd. + * + * For purchase of APITable Enterprise Edition license, please contact . + */ + +package com.apitable.player.config.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * notification properties. + */ +@Data +@ConfigurationProperties(prefix = "notification") +public class NotificationProperties { + private String callbackUrl; + + private String templateIds; +} + diff --git a/backend-server/application/src/main/java/com/apitable/player/service/impl/PlayerNotificationServiceImpl.java b/backend-server/application/src/main/java/com/apitable/player/service/impl/PlayerNotificationServiceImpl.java index 5f2e951e47..2fa7bcac9b 100644 --- a/backend-server/application/src/main/java/com/apitable/player/service/impl/PlayerNotificationServiceImpl.java +++ b/backend-server/application/src/main/java/com/apitable/player/service/impl/PlayerNotificationServiceImpl.java @@ -72,6 +72,7 @@ import com.apitable.shared.component.TaskManager; import com.apitable.shared.component.notification.INotificationFactory; import com.apitable.shared.component.notification.NotificationHelper; +import com.apitable.shared.component.notification.NotificationManager; import com.apitable.shared.component.notification.NotificationRenderMap; import com.apitable.shared.component.notification.NotificationTemplateId; import com.apitable.shared.component.notification.NotificationToTag; @@ -552,6 +553,7 @@ public boolean setDeletedIsTrue(String[] ids) { return baseMapper.deleteNotificationByIds(ids); } + @Override public boolean createBatch(List notifyEntities, List createEntities) { @@ -562,7 +564,10 @@ public boolean createBatch(List notifyEntities, // Therefore, if there is a key, no message will be sent, but the number of messages will be increased. } if (CollUtil.isNotEmpty(createEntities)) { - return SqlHelper.retBool(baseMapper.insertBatch(createEntities)); + boolean result = SqlHelper.retBool(baseMapper.insertBatch(createEntities)); + TaskManager.me().execute(() -> NotificationManager.me().doCallback(notifyEntities)); + return result; + } return true; } @@ -740,4 +745,4 @@ private Dict formatEmailDetailVo(NotificationCreateRo ro) { } return dict; } -} +} \ No newline at end of file diff --git a/backend-server/application/src/main/java/com/apitable/shared/component/notification/NotificationManager.java b/backend-server/application/src/main/java/com/apitable/shared/component/notification/NotificationManager.java index f5f3d2e0ad..22f0099962 100644 --- a/backend-server/application/src/main/java/com/apitable/shared/component/notification/NotificationManager.java +++ b/backend-server/application/src/main/java/com/apitable/shared/component/notification/NotificationManager.java @@ -22,8 +22,12 @@ import cn.hutool.core.convert.Convert; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import com.apitable.core.util.SpringContextHolder; +import com.apitable.interfaces.social.facade.SocialServiceFacade; +import com.apitable.player.config.properties.NotificationProperties; +import com.apitable.player.entity.PlayerNotificationEntity; import com.apitable.player.ro.NotificationCreateRo; import com.apitable.player.service.impl.PlayerNotificationServiceImpl; import com.apitable.shared.cache.service.LoginUserCacheService; @@ -32,10 +36,15 @@ import com.apitable.shared.constants.NotificationConstants; import com.apitable.starter.socketio.core.SocketClientTemplate; import jakarta.annotation.Resource; +import java.util.Arrays; +import java.util.HashMap; import java.util.List; import java.util.Map; import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; import org.springframework.stereotype.Component; +import org.springframework.web.client.RestClient; /** * notification manager. @@ -59,6 +68,15 @@ public class NotificationManager { @Resource private MessagingCenterNotifyObserver messagingCenterNotifyObserver; + @Resource + private NotificationProperties notificationProperties; + + @Resource + private RestClient restClient; + + @Resource + private SocialServiceFacade socialServiceFacade; + public static NotificationManager me() { return SpringContextHolder.getBean(NotificationManager.class); } @@ -144,4 +162,61 @@ public void centerNotify(NotificationCreateRo ro) { centerSub.addObserver(messagingCenterNotifyObserver); centerSub.send(ro); } + + /** + * notification call back. + */ + public void doCallback(List entities) { + String callbackUrl = notificationProperties.getCallbackUrl(); + if (StrUtil.isBlank(callbackUrl)) { + return; + } + List filterTemplateIds = + Arrays.stream(notificationProperties.getTemplateIds().split(",")).toList(); + List fromUserIds = ListUtil.toList(); + List toUserIds = ListUtil.toList(); + entities.forEach(i -> { + if (!i.getFromUser().equals(0L)) { + fromUserIds.add(i.getFromUser()); + } + toUserIds.add(i.getToUser()); + }); + Map fromUserMap = socialServiceFacade.getUnionIdMap(fromUserIds); + Map toUserMap = socialServiceFacade.getUnionIdMap(toUserIds); + List> dataList = ListUtil.toList(); + entities.forEach(entity -> { + if (!filterTemplateIds.contains(entity.getTemplateId())) { + return; + } + JSONObject body = JSONUtil.parseObj(entity.getNotifyBody()); + // remove null fields + Map data = new HashMap<>(); + data.put("spaceId", entity.getSpaceId()); + data.put("nodeId", entity.getNodeId()); + data.put("embedId", body.getByPath("extras.linkId")); + data.put("templateId", entity.getTemplateId()); + data.put("fromUserId", entity.getFromUser()); + data.put("fromUserUnionId", fromUserMap.get(entity.getFromUser())); + data.put("toUserId", entity.getToUser()); + data.put("toUserUnionId", toUserMap.get(entity.getToUser())); + data.put("notifyBody", body); // json string + dataList.add(data); + }); + if (dataList.isEmpty()) { + return; + } + Map paramMap = new HashMap<>(); + paramMap.put("data", dataList); + + HttpHeaders header = new HttpHeaders(); + header.setContentType(MediaType.APPLICATION_JSON); + // post request needs to be set contentType + RestClient.ResponseSpec res = restClient + .post() + .uri(callbackUrl) + .headers(headers -> headers.addAll(header)) + .body(paramMap) + .retrieve(); + log.info("doCallback:{}", res); + } } diff --git a/backend-server/application/src/main/java/com/apitable/shared/component/notification/observer/AbstractNotifyObserver.java b/backend-server/application/src/main/java/com/apitable/shared/component/notification/observer/AbstractNotifyObserver.java index 927a6fd9b8..b5165a2c81 100644 --- a/backend-server/application/src/main/java/com/apitable/shared/component/notification/observer/AbstractNotifyObserver.java +++ b/backend-server/application/src/main/java/com/apitable/shared/component/notification/observer/AbstractNotifyObserver.java @@ -29,12 +29,12 @@ import cn.hutool.json.JSONUtil; import com.apitable.organization.service.IMemberService; import com.apitable.player.ro.NotificationCreateRo; +import com.apitable.shared.clock.spring.ClockManager; import com.apitable.shared.component.notification.NotificationHelper; import com.apitable.shared.sysconfig.i18n.I18nStringsUtil; import com.apitable.space.service.ISpaceService; import com.apitable.workspace.service.INodeService; import jakarta.annotation.Resource; -import java.time.Instant; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.HashMap; @@ -43,9 +43,7 @@ import java.util.Objects; /** - *

* base notify observer. - *

* * @author zoe zheng */ @@ -92,8 +90,8 @@ public Map bindingMap(NotificationCreateRo ro) { if (extras != null) { extras.forEach((k, v) -> { if (StrUtil.endWith(k, "At")) { - LocalDateTime dateTime = DateUtil.toLocalDateTime( - Instant.ofEpochMilli(Long.parseLong(v.toString()))); + LocalDateTime dateTime = + ClockManager.me().convertMillis(Long.parseLong(v.toString())); bindingMap.put(k, DateUtil.format(dateTime, NORM_DATETIME_MINUTE_PATTERN)); } else if (Objects.equals(k, INVOLVE_RECORD_IDS)) { bindingMap.put(StrUtil.toCamelCase(EMAIL_RECORD_ID), diff --git a/backend-server/application/src/main/java/com/apitable/shared/config/properties/ConstProperties.java b/backend-server/application/src/main/java/com/apitable/shared/config/properties/ConstProperties.java index f217b3c880..52fd0facd1 100644 --- a/backend-server/application/src/main/java/com/apitable/shared/config/properties/ConstProperties.java +++ b/backend-server/application/src/main/java/com/apitable/shared/config/properties/ConstProperties.java @@ -92,11 +92,6 @@ public class ConstProperties { private String emailVerificationUrl = "/user/email_verification"; - /** - * max invited record for a single day. - */ - private Integer maxInviteCountForFree = 10; - public OssBucketInfo getOssBucketByAsset() { return Optional.ofNullable(ossBuckets).orElseGet(HashMap::new) diff --git a/backend-server/application/src/main/java/com/apitable/shared/config/properties/LimitProperties.java b/backend-server/application/src/main/java/com/apitable/shared/config/properties/LimitProperties.java index c0edc85af4..16bd3bb6d5 100644 --- a/backend-server/application/src/main/java/com/apitable/shared/config/properties/LimitProperties.java +++ b/backend-server/application/src/main/java/com/apitable/shared/config/properties/LimitProperties.java @@ -128,4 +128,9 @@ public class LimitProperties { * max limit of action count. */ private Integer automationActionCount = 9; + + /** + * max invited record for a single day. + */ + private Integer maxInviteCountForFree = 10; } diff --git a/backend-server/application/src/main/java/com/apitable/shared/security/FrequencyLimitServiceImpl.java b/backend-server/application/src/main/java/com/apitable/shared/security/FrequencyLimitServiceImpl.java new file mode 100644 index 0000000000..aea6ebfa83 --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/shared/security/FrequencyLimitServiceImpl.java @@ -0,0 +1,52 @@ +package com.apitable.shared.security; + +import cn.hutool.core.date.DateUnit; +import cn.hutool.core.date.DateUtil; +import com.apitable.core.constants.RedisConstants; +import com.apitable.core.exception.BusinessException; +import com.apitable.interfaces.billing.facade.EntitlementServiceFacade; +import com.apitable.interfaces.billing.model.SubscriptionInfo; +import com.apitable.shared.config.properties.LimitProperties; +import jakarta.annotation.Resource; +import java.util.Objects; +import java.util.concurrent.TimeUnit; +import org.springframework.data.redis.core.BoundSetOperations; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; + +/** + * Frequency Limit Service Implementation. + */ +@Service +public class FrequencyLimitServiceImpl implements IFrequencyLimitService { + + @Resource + private EntitlementServiceFacade entitlementServiceFacade; + + @Resource + private LimitProperties limitProperties; + + @Resource + private RedisTemplate redisTemplate; + + @Override + public void spaceInviteFrequency(String spaceId) { + SubscriptionInfo subscriptionInfo = + entitlementServiceFacade.getSpaceSubscription(spaceId); + var seatNums = subscriptionInfo.getFeature().getSeat(); + if (!subscriptionInfo.isFree() || seatNums.isUnlimited()) { + return; + } + String repeatKey = RedisConstants.getGeneralFrequencyRecordOfInvite(spaceId); + BoundSetOperations ops = redisTemplate.boundSetOps(repeatKey); + ops.add(DateUtil.current()); + ops.expire(1, TimeUnit.DAYS); + + long repeatNum = Objects.requireNonNull(ops.members()).stream() + .filter(val -> DateUtil.between(DateUtil.date(), DateUtil.date(val), DateUnit.DAY) < 1) + .count(); + if (repeatNum > limitProperties.getMaxInviteCountForFree()) { + throw new BusinessException("Frequent operation"); + } + } +} diff --git a/backend-server/application/src/main/java/com/apitable/shared/security/IFrequencyLimitService.java b/backend-server/application/src/main/java/com/apitable/shared/security/IFrequencyLimitService.java new file mode 100644 index 0000000000..bb5377e65d --- /dev/null +++ b/backend-server/application/src/main/java/com/apitable/shared/security/IFrequencyLimitService.java @@ -0,0 +1,8 @@ +package com.apitable.shared.security; + +/** + * Frequency Limit Service. + */ +public interface IFrequencyLimitService { + void spaceInviteFrequency(String spaceId); +} diff --git a/backend-server/application/src/main/java/com/apitable/space/controller/SpaceInvitationController.java b/backend-server/application/src/main/java/com/apitable/space/controller/SpaceInvitationController.java index 01581fa3ad..6bc480c831 100644 --- a/backend-server/application/src/main/java/com/apitable/space/controller/SpaceInvitationController.java +++ b/backend-server/application/src/main/java/com/apitable/space/controller/SpaceInvitationController.java @@ -42,6 +42,7 @@ import com.apitable.shared.constants.ParamsConstants; import com.apitable.shared.context.LoginContext; import com.apitable.shared.context.SessionContext; +import com.apitable.shared.security.IFrequencyLimitService; import com.apitable.space.ro.EmailInvitationMemberRo; import com.apitable.space.ro.EmailInvitationResendRo; import com.apitable.space.ro.EmailInvitationRo; @@ -89,6 +90,9 @@ public class SpaceInvitationController { @Resource private BlackListServiceFacade blackListServiceFacade; + @Resource + private IFrequencyLimitService iFrequencyLimitService; + @Resource private HumanVerificationServiceFacade humanVerificationServiceFacade; @@ -157,13 +161,14 @@ public ResponseData sendEmailInvitation( if (StrUtil.isBlank(spaceId)) { spaceId = LoginContext.me().getSpaceId(); } - // human verification - humanVerificationServiceFacade.verifyNonRobot(new NonRobotMetadata(data.getData())); // whether in black list blackListServiceFacade.checkSpace(spaceId); + iFrequencyLimitService.spaceInviteFrequency(spaceId); iSpaceService.checkSeatOverLimit(spaceId, data.getInvite().size()); // check whether space can invite user iSpaceService.checkCanOperateSpaceUpdate(spaceId); + // human verification + humanVerificationServiceFacade.verifyNonRobot(new NonRobotMetadata(data.getData())); List inviteMembers = data.getInvite(); EmailInvitationResultVO view = EmailInvitationResultVO.builder().build(); // get invited emails @@ -212,6 +217,7 @@ public ResponseData resendEmailInvitation( } // check black space blackListServiceFacade.checkSpace(spaceId); + iFrequencyLimitService.spaceInviteFrequency(spaceId); iSpaceService.checkSeatOverLimit(spaceId); iSpaceService.checkCanOperateSpaceUpdate(spaceId); // Again email invite members diff --git a/backend-server/application/src/main/java/com/apitable/space/service/impl/SpaceServiceImpl.java b/backend-server/application/src/main/java/com/apitable/space/service/impl/SpaceServiceImpl.java index d7b82200d1..157086f5a5 100644 --- a/backend-server/application/src/main/java/com/apitable/space/service/impl/SpaceServiceImpl.java +++ b/backend-server/application/src/main/java/com/apitable/space/service/impl/SpaceServiceImpl.java @@ -248,6 +248,9 @@ public class SpaceServiceImpl extends ServiceImpl @Value("${SKIP_USAGE_VERIFICATION:false}") private Boolean skipUsageVerification; + @Value("${SKIP_API_USAGE_VERIFICATION:false}") + private Boolean skipApiUsageVerification; + @Override public SpaceEntity getEntityBySpaceId(String spaceId) { return baseMapper.selectBySpaceId(spaceId); @@ -729,6 +732,23 @@ public SeatUsage getSeatUsageForIM(String spaceId) { public SpaceInfoVO getSpaceInfo(final String spaceId) { SpaceEntity entity = getBySpaceId(spaceId); SpaceInfoVO spaceInfoVO = this.transform(entity); + // obtain third party information + SocialConnectInfo socialConnectInfo = + socialServiceFacade.getConnectInfo(spaceId); + SpaceSocialConfig bindInfo = new SpaceSocialConfig(); + if (ObjectUtil.isNotNull(socialConnectInfo) && socialConnectInfo.isEnabled()) { + bindInfo.setEnabled(true); + bindInfo.setPlatform(socialConnectInfo.getPlatform()); + bindInfo.setAppType(socialConnectInfo.getAppType()); + bindInfo.setAuthMode(socialConnectInfo.getAuthMode()); + // is it synchronizing the contact + bindInfo.setContactSyncing(socialConnectInfo.contactSyncing()); + } + spaceInfoVO.setSocial(bindInfo); + // chat bot status + CommonCacheService cacheService = SpringContextHolder.getBean(CommonCacheService.class); + boolean isEnableChatbot = cacheService.checkIfSpaceEnabledChatbot(spaceId); + spaceInfoVO.setIsEnableChatbot(isEnableChatbot); if (Boolean.TRUE.equals(skipUsageVerification)) { spaceInfoVO.setSocial(new SpaceSocialConfig()); spaceInfoVO.setSeatUsage(new SeatUsage()); @@ -805,29 +825,11 @@ public SpaceInfoVO getSpaceInfo(final String spaceId) { spaceCapacityUsedInfo.getCurrentBundleCapacityUsedSizes()); spaceInfoVO.setGiftCapacityUsedSizes( spaceCapacityUsedInfo.getGiftCapacityUsedSizes()); - - // obtain third party information - SocialConnectInfo socialConnectInfo = - socialServiceFacade.getConnectInfo(spaceId); - SpaceSocialConfig bindInfo = new SpaceSocialConfig(); - if (ObjectUtil.isNotNull(socialConnectInfo) && socialConnectInfo.isEnabled()) { - bindInfo.setEnabled(true); - bindInfo.setPlatform(socialConnectInfo.getPlatform()); - bindInfo.setAppType(socialConnectInfo.getAppType()); - bindInfo.setAuthMode(socialConnectInfo.getAuthMode()); - // is it synchronizing the contact - bindInfo.setContactSyncing(socialConnectInfo.contactSyncing()); - } - spaceInfoVO.setSocial(bindInfo); // credit BigDecimal usedCredit = aiServiceFacade.getUsedCreditCount(spaceId, dateRange.getCycleStartDate(), dateRange.getCycleEndDate()); spaceInfoVO.setUsedCredit(usedCredit); - // chat bot status - CommonCacheService cacheService = SpringContextHolder.getBean(CommonCacheService.class); - boolean isEnableChatbot = cacheService.checkIfSpaceEnabledChatbot(spaceId); - spaceInfoVO.setIsEnableChatbot(isEnableChatbot); return spaceInfoVO; } diff --git a/backend-server/application/src/main/java/com/apitable/space/service/impl/StaticsServiceImpl.java b/backend-server/application/src/main/java/com/apitable/space/service/impl/StaticsServiceImpl.java index 367363b4d7..c3f0eaf047 100644 --- a/backend-server/application/src/main/java/com/apitable/space/service/impl/StaticsServiceImpl.java +++ b/backend-server/application/src/main/java/com/apitable/space/service/impl/StaticsServiceImpl.java @@ -100,6 +100,9 @@ public class StaticsServiceImpl implements IStaticsService { @Value("${SKIP_USAGE_VERIFICATION:false}") private Boolean skipUsageVerification; + @Value("${SKIP_API_USAGE_VERIFICATION:false}") + private Boolean skipApiUsageVerification; + @Value("${SPACE_STATISTICS_CACHE_HOURS:1}") private Integer cacheHours; @@ -108,6 +111,9 @@ public long getCurrentMonthApiUsage(String spaceId, LocalDate currentMonth) { if (Boolean.TRUE.equals(skipUsageVerification)) { return 0; } + if (Boolean.TRUE.equals(skipApiUsageVerification)) { + return 0; + } // Get the API usage of this month up to yesterday Long apiUsageUntilYesterday = this.getCurrentMonthApiUsageUntilYesterday(spaceId, currentMonth); diff --git a/backend-server/application/src/main/java/com/apitable/workspace/controller/NodeController.java b/backend-server/application/src/main/java/com/apitable/workspace/controller/NodeController.java index 7bae5c3908..b78f314bfe 100644 --- a/backend-server/application/src/main/java/com/apitable/workspace/controller/NodeController.java +++ b/backend-server/application/src/main/java/com/apitable/workspace/controller/NodeController.java @@ -66,6 +66,7 @@ import com.apitable.shared.holder.SpaceHolder; import com.apitable.shared.listener.event.AuditSpaceEvent; import com.apitable.shared.listener.event.AuditSpaceEvent.AuditSpaceArg; +import com.apitable.shared.util.IdUtil; import com.apitable.shared.util.information.ClientOriginInfo; import com.apitable.shared.util.information.InformationUtil; import com.apitable.space.enums.AuditSpaceAction; @@ -931,9 +932,11 @@ public ResponseData remind(@RequestBody @Valid RemindMemberRo ro) { LoginContext.me().getUserSpaceDto(spaceId); } else { // node sharing - String shareSpaceId = nodeShareSettingMapper.selectSpaceIdByShareId(ro.getLinkId()); - ExceptionUtil.isNotNull(shareSpaceId, NodeException.SHARE_EXPIRE); - ExceptionUtil.isTrue(shareSpaceId.equals(spaceId), SpaceException.NOT_IN_SPACE); + if (!IdUtil.isEmbed(ro.getLinkId())) { + String shareSpaceId = nodeShareSettingMapper.selectSpaceIdByShareId(ro.getLinkId()); + ExceptionUtil.isNotNull(shareSpaceId, NodeException.SHARE_EXPIRE); + ExceptionUtil.isTrue(shareSpaceId.equals(spaceId), SpaceException.NOT_IN_SPACE); + } } if (iNodeService.nodePrivate(ro.getNodeId())) { return ResponseData.success(); diff --git a/packages/datasheet/src/pc/components/calendar_view/utils.ts b/backend-server/application/src/main/java/com/apitable/workspace/dto/DatasheetMetaColumnDTO.java similarity index 76% rename from packages/datasheet/src/pc/components/calendar_view/utils.ts rename to backend-server/application/src/main/java/com/apitable/workspace/dto/DatasheetMetaColumnDTO.java index 2e9feb3ee8..49a3c27c52 100644 --- a/packages/datasheet/src/pc/components/calendar_view/utils.ts +++ b/backend-server/application/src/main/java/com/apitable/workspace/dto/DatasheetMetaColumnDTO.java @@ -1,4 +1,4 @@ -/** +/* * APITable * Copyright (C) 2022 APITable Ltd. * @@ -16,10 +16,17 @@ * along with this program. If not, see . */ -export const formatString2Date = (value: string) => { - const str = value.replace(/年| /, '-').replace(/月/, ''); - const parts = str.split('-'); - const year = parts[0]; - const month = parts[1].padStart(2, '0'); - return `${year}-${month}`; -}; +package com.apitable.workspace.dto; + +import lombok.Data; + +/** + * Datasheet Meta Column DTO. + */ +@Data +public class DatasheetMetaColumnDTO { + + private String dstId; + + private Integer mdFieldMapSize; +} diff --git a/backend-server/application/src/main/java/com/apitable/workspace/dto/NodeShareDTO.java b/backend-server/application/src/main/java/com/apitable/workspace/dto/NodeShareDTO.java index 086811f4e4..d9dcf10c68 100644 --- a/backend-server/application/src/main/java/com/apitable/workspace/dto/NodeShareDTO.java +++ b/backend-server/application/src/main/java/com/apitable/workspace/dto/NodeShareDTO.java @@ -33,4 +33,6 @@ public class NodeShareDTO { private String spaceId; private Long operator; + + private Boolean isEnabled; } diff --git a/backend-server/application/src/main/java/com/apitable/workspace/entity/DatasheetRecordAlarmEntity.java b/backend-server/application/src/main/java/com/apitable/workspace/entity/DatasheetRecordAlarmEntity.java index f31ee5b1ad..0b57d0768e 100644 --- a/backend-server/application/src/main/java/com/apitable/workspace/entity/DatasheetRecordAlarmEntity.java +++ b/backend-server/application/src/main/java/com/apitable/workspace/entity/DatasheetRecordAlarmEntity.java @@ -72,6 +72,11 @@ public class DatasheetRecordAlarmEntity implements Serializable { */ private String dstId; + /** + * Resource ID(link#node#node_id). + */ + private String resourceId; + /** * Record ID(link#datasheet_record#record_id). */ diff --git a/backend-server/application/src/main/java/com/apitable/workspace/mapper/DatasheetMetaMapper.java b/backend-server/application/src/main/java/com/apitable/workspace/mapper/DatasheetMetaMapper.java index 66f7656f61..8a233999f2 100644 --- a/backend-server/application/src/main/java/com/apitable/workspace/mapper/DatasheetMetaMapper.java +++ b/backend-server/application/src/main/java/com/apitable/workspace/mapper/DatasheetMetaMapper.java @@ -19,6 +19,7 @@ package com.apitable.workspace.mapper; import com.apitable.internal.dto.SimpleDatasheetMetaDTO; +import com.apitable.workspace.dto.DatasheetMetaColumnDTO; import com.apitable.workspace.dto.DatasheetMetaDTO; import com.apitable.workspace.dto.DatasheetSnapshot; import com.apitable.workspace.entity.DatasheetMetaEntity; @@ -57,7 +58,15 @@ public interface DatasheetMetaMapper extends BaseMapper { SimpleDatasheetMetaDTO selectByNodeId(@Param("dstId") String dstId); /** - * quert DTO by datasheet ids. + * query meta column DTO. + * + * @param dstIds datasheet ids + * @return DatasheetMetaColumnDTO + */ + List selectMetaColumnDtoByDstIds(@Param("dstIds") Collection dstIds); + + /** + * query DTO by datasheet ids. * * @param dstIdList datasheet ids * @return DatasheetMetaDTO diff --git a/backend-server/application/src/main/java/com/apitable/workspace/mapper/NodeMapper.java b/backend-server/application/src/main/java/com/apitable/workspace/mapper/NodeMapper.java index e9ad1d3cf6..afc69bf24b 100644 --- a/backend-server/application/src/main/java/com/apitable/workspace/mapper/NodeMapper.java +++ b/backend-server/application/src/main/java/com/apitable/workspace/mapper/NodeMapper.java @@ -155,6 +155,7 @@ List selectNodeIdBySpaceIdAndNodeNameLikeIncludeDeleted( * @param memberId member id * @return NodeInfoVos */ + @Deprecated List selectNodeInfoByNodeIds(@Param("nodeIds") Collection nodeIds, @Param("memberId") Long memberId); @@ -165,6 +166,7 @@ List selectNodeInfoByNodeIds(@Param("nodeIds") Collection no * @param memberId member id * @return NodeInfoTreeVo */ + @Deprecated List selectNodeInfoTreeByNodeIds(@Param("nodeIds") Collection nodeIds, @Param("memberId") Long memberId); @@ -222,6 +224,14 @@ List selectNodeTreeDTOByParentIdIn( @Param("parentIds") Collection parentIds, @Param("isRubbish") Boolean isRubbish, @Param("unitIds") List unitIds); + /** + * Query parent id with children nodes. + * + * @param parentIds parent node ids + * @return parent node ids + */ + List selectParentIdWithChildren(@Param("parentIds") Collection parentIds); + /** * Query the ID of the direct child node. * diff --git a/backend-server/application/src/main/java/com/apitable/workspace/mapper/NodeShareSettingMapper.java b/backend-server/application/src/main/java/com/apitable/workspace/mapper/NodeShareSettingMapper.java index ddfde95fa9..103f1e8311 100644 --- a/backend-server/application/src/main/java/com/apitable/workspace/mapper/NodeShareSettingMapper.java +++ b/backend-server/application/src/main/java/com/apitable/workspace/mapper/NodeShareSettingMapper.java @@ -21,6 +21,7 @@ import com.apitable.workspace.dto.NodeShareDTO; import com.apitable.workspace.entity.NodeShareSettingEntity; import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import java.util.Collection; import java.util.List; import org.apache.ibatis.annotations.Param; @@ -107,7 +108,7 @@ public interface NodeShareSettingMapper extends BaseMapper selectDtoByNodeIds(@Param("nodeIds") List nodeIds); + List selectDtoByNodeIds(@Param("nodeIds") Collection nodeIds); /** * Find the list of shared node IDs last modified by the specified member. diff --git a/backend-server/application/src/main/java/com/apitable/workspace/service/IDatasheetMetaService.java b/backend-server/application/src/main/java/com/apitable/workspace/service/IDatasheetMetaService.java index 190507d201..97fb7fd694 100644 --- a/backend-server/application/src/main/java/com/apitable/workspace/service/IDatasheetMetaService.java +++ b/backend-server/application/src/main/java/com/apitable/workspace/service/IDatasheetMetaService.java @@ -19,12 +19,12 @@ package com.apitable.workspace.service; import com.apitable.internal.dto.SimpleDatasheetMetaDTO; +import com.apitable.workspace.dto.DatasheetMetaColumnDTO; import com.apitable.workspace.dto.DatasheetMetaDTO; import com.apitable.workspace.dto.DatasheetSnapshot; import com.apitable.workspace.entity.DatasheetMetaEntity; import com.apitable.workspace.ro.MetaOpRo; import java.util.List; -import org.apache.ibatis.annotations.Param; /** * datasheet meta service. @@ -46,13 +46,22 @@ public interface IDatasheetMetaService { */ SimpleDatasheetMetaDTO findByDstId(String dstId); + /** + * get meta column dto list. + * + * @param dstIds datasheet ids + * @return DatasheetMetaColumnDTO List + */ + List findMetaColumnDTOs(List dstIds); + + /** * get dto by datasheet id list. * * @param dstIds datasheet ids * @return DatasheetMetaDTO */ - List findMetaDtoByDstIds(@Param("list") List dstIds); + List findMetaDtoByDstIds(List dstIds); /** * create. diff --git a/backend-server/application/src/main/java/com/apitable/workspace/service/impl/ControlMemberServiceImpl.java b/backend-server/application/src/main/java/com/apitable/workspace/service/impl/ControlMemberServiceImpl.java index 5e326e9ad0..2e766d972c 100644 --- a/backend-server/application/src/main/java/com/apitable/workspace/service/impl/ControlMemberServiceImpl.java +++ b/backend-server/application/src/main/java/com/apitable/workspace/service/impl/ControlMemberServiceImpl.java @@ -35,6 +35,8 @@ import com.apitable.organization.enums.UnitType; import com.apitable.organization.service.IMemberService; import com.apitable.organization.service.IRoleMemberService; +import com.apitable.organization.service.ITagMemberRelService; +import com.apitable.organization.service.ITagService; import com.apitable.organization.service.ITeamService; import com.apitable.organization.service.IUnitService; import com.apitable.shared.util.page.PageHelper; @@ -86,6 +88,9 @@ public class ControlMemberServiceImpl implements IControlMemberService { @Resource private IControlRoleService iControlRoleService; + @Resource + private ITagService iTagService; + @Override public PageInfo getControlRoleMemberPageInfo( Page page, String spaceId, ControlId controlId, Class clz @@ -143,6 +148,7 @@ public Map getMemberControlRoleMap(String spaceId, List memberIds = new ArrayList<>(); List teamIds = new ArrayList<>(); List roleIds = new ArrayList<>(); + List tagIds = new ArrayList<>(); for (ControlRoleUnitDTO control : entry.getValue()) { UnitType unitType = UnitType.toEnum(control.getUnitType()); switch (unitType) { @@ -155,6 +161,9 @@ public Map getMemberControlRoleMap(String spaceId, case ROLE: roleIds.add(control.getUnitRefId()); break; + case TAG: + tagIds.add(control.getUnitRefId()); + break; default: break; } @@ -170,12 +179,17 @@ public Map getMemberControlRoleMap(String spaceId, List roleMemberIds = iRoleMemberService.getMemberIdsByRoleIds(roleIds); memberIds.addAll(roleMemberIds); } + if (CollUtil.isNotEmpty(tagIds)) { + List tagMemberIds = iTagService.getMemberIdsByTagIds(tagIds); + memberIds.addAll(tagMemberIds); + } for (Long memberId : memberIds) { if (memberRoleMap.containsKey(memberId)) { continue; } memberRoleMap.put(memberId, new ControlMemberDTO(memberId, entry.getKey())); } + } return memberRoleMap; } diff --git a/backend-server/application/src/main/java/com/apitable/workspace/service/impl/DatasheetMetaServiceImpl.java b/backend-server/application/src/main/java/com/apitable/workspace/service/impl/DatasheetMetaServiceImpl.java index 819bfb10c1..0cc3f128d4 100644 --- a/backend-server/application/src/main/java/com/apitable/workspace/service/impl/DatasheetMetaServiceImpl.java +++ b/backend-server/application/src/main/java/com/apitable/workspace/service/impl/DatasheetMetaServiceImpl.java @@ -24,6 +24,8 @@ import com.apitable.core.util.ExceptionUtil; import com.apitable.core.util.SqlTool; import com.apitable.internal.dto.SimpleDatasheetMetaDTO; +import com.apitable.shared.util.DBUtil; +import com.apitable.workspace.dto.DatasheetMetaColumnDTO; import com.apitable.workspace.dto.DatasheetMetaDTO; import com.apitable.workspace.dto.DatasheetSnapshot; import com.apitable.workspace.dto.DatasheetSnapshot.View; @@ -68,6 +70,12 @@ public SimpleDatasheetMetaDTO findByDstId(String dstId) { return meta; } + @Override + public List findMetaColumnDTOs(List dstIds) { + return DBUtil.batchSelectByFieldIn(dstIds, + datasheetMetaMapper::selectMetaColumnDtoByDstIds, 100); + } + @Override public List findMetaDtoByDstIds(List dstIds) { List dtoList = new ArrayList<>(); diff --git a/backend-server/application/src/main/java/com/apitable/workspace/service/impl/DatasheetServiceImpl.java b/backend-server/application/src/main/java/com/apitable/workspace/service/impl/DatasheetServiceImpl.java index d797b51d98..e47c2d19d9 100644 --- a/backend-server/application/src/main/java/com/apitable/workspace/service/impl/DatasheetServiceImpl.java +++ b/backend-server/application/src/main/java/com/apitable/workspace/service/impl/DatasheetServiceImpl.java @@ -964,6 +964,7 @@ public void remindMemberRecOp(Long userId, String spaceId, RemindMemberRo ro) { .set("fieldName", remindUnitRecRo.getFieldName()) .set("recordTitle", recordTitle) .set("viewId", ro.getViewId()) + .set("linkId", ro.getLinkId()) .set("recordIds", remindUnitRecRo.getRecordIds()); if (null != ro.getExtra() && null != ro.getExtra().getContent()) { // comments diff --git a/backend-server/application/src/main/java/com/apitable/workspace/service/impl/FieldRoleServiceImpl.java b/backend-server/application/src/main/java/com/apitable/workspace/service/impl/FieldRoleServiceImpl.java index 5a030b5d72..38a6446c65 100644 --- a/backend-server/application/src/main/java/com/apitable/workspace/service/impl/FieldRoleServiceImpl.java +++ b/backend-server/application/src/main/java/com/apitable/workspace/service/impl/FieldRoleServiceImpl.java @@ -54,11 +54,14 @@ import com.apitable.organization.service.IOrganizationService; import com.apitable.organization.service.IRoleMemberService; import com.apitable.organization.service.IRoleService; +import com.apitable.organization.service.ITagService; import com.apitable.organization.service.ITeamService; import com.apitable.organization.vo.MemberTeamPathInfo; import com.apitable.organization.vo.RoleInfoVo; +import com.apitable.organization.vo.TagInfoVo; import com.apitable.organization.vo.UnitMemberVo; import com.apitable.organization.vo.UnitTeamVo; +import com.apitable.shared.util.IdUtil; import com.apitable.shared.util.page.PageInfo; import com.apitable.space.service.ISpaceRoleService; import com.apitable.workspace.dto.ControlRoleInfo; @@ -84,6 +87,7 @@ import com.apitable.workspace.vo.FieldRoleMemberVo; import com.apitable.workspace.vo.FieldRoleSetting; import com.apitable.workspace.vo.NodeRoleMemberVo; +import com.apitable.workspace.vo.NodeRoleUnit; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; @@ -169,6 +173,9 @@ public class FieldRoleServiceImpl implements IFieldRoleService { @Resource private IRoleMemberService iRoleMemberService; + @Resource + private ITagService iTagService; + @Override public boolean getFieldRoleEnabledStatus(String dstId, String fieldId) { ControlId controlId = ControlIdBuilder.fieldId(dstId, fieldId); @@ -313,6 +320,7 @@ public FieldCollaboratorVO getFieldRoles(String datasheetId, String fieldId) { List teamIds = new ArrayList<>(); List memberIds = new ArrayList<>(); List roleIds = new ArrayList<>(); + List tagIds = new ArrayList<>(); for (ControlRoleUnitDTO control : entry.getValue()) { if (unitIdToFieldRoleMap.containsKey(control.getUnitId())) { unitIdToFieldRoleMap.get(control.getUnitId()).setPermissionExtend(false); @@ -329,6 +337,8 @@ public FieldCollaboratorVO getFieldRoles(String datasheetId, String fieldId) { memberIds.add(control.getUnitRefId()); } else if (unitType == UnitType.ROLE) { roleIds.add(control.getUnitRefId()); + } else if (unitType == UnitType.TAG) { + tagIds.add(control.getUnitRefId()); } role.setCanRead(true); role.setCanEdit(true); @@ -363,6 +373,15 @@ public FieldCollaboratorVO getFieldRoles(String datasheetId, String fieldId) { role.setUnitRefId(roleVo.getRoleId()); } } + if (!tagIds.isEmpty()) { + List tags = iTagService.getTagVos(spaceId, tagIds); + for (TagInfoVo tag : tags) { + FieldRole role = unitIdToFieldRoleMap.get(tag.getUnitId()); + role.setUnitRefId(tag.getTagId()); + role.setUnitName(tag.getTagName()); + role.setMemberCount(tag.getMemberCount()); + } + } } fieldCollaboratorVO.setRoles(ListUtil.toList(unitIdToFieldRoleMap.values())); return fieldCollaboratorVO; @@ -511,13 +530,11 @@ public FieldPermissionView getFieldPermissionView(Long memberId, String nodeId, .collect(Collectors.toMap(String::toString, controlId -> controlId.substring(controlId.indexOf(ControlIdBuilder.SYMBOL) + 1))); // load field permissions in sharing - if (StrUtil.isNotBlank(shareId)) { + if ((StrUtil.isNotBlank(shareId) && !IdUtil.isEmbed(shareId)) || null == memberId) { // If the datasheet is not collected, directly return the field with the permission set. if (type != NodeType.FORM) { - Map permissionInfoMap = - controlIdToFieldIdMap.values().stream() - .collect(Collectors.toMap(String::toString, - fieldId -> FieldPermissionInfo.builder() + Map permissionInfoMap = controlIdToFieldIdMap.values().stream() + .collect(Collectors.toMap(String::toString, fieldId -> FieldPermissionInfo.builder() .fieldId(fieldId) .permission(new FieldPermission()) .build())); @@ -743,6 +760,14 @@ private void populateFieldRoles(Map unitIdToFieldRoleMap, fieldRole.setMemberCount(role.getMemberCount()); fieldRole.setUnitRefId(role.getRoleId()); }); + } else if (key == UnitType.TAG) { + List tagVos = iTagService.getTagVos(spcId, unitRefIds); + tagVos.forEach(tag -> { + FieldRole fieldRole = unitIdToFieldRoleMap.get(tag.getUnitId()); + fieldRole.setUnitRefId(tag.getTagId()); + fieldRole.setUnitName(tag.getTagName()); + fieldRole.setMemberCount(tag.getMemberCount()); + }); } }); } diff --git a/backend-server/application/src/main/java/com/apitable/workspace/service/impl/NodeRoleServiceImpl.java b/backend-server/application/src/main/java/com/apitable/workspace/service/impl/NodeRoleServiceImpl.java index b13f1ad8de..f0f60f154d 100644 --- a/backend-server/application/src/main/java/com/apitable/workspace/service/impl/NodeRoleServiceImpl.java +++ b/backend-server/application/src/main/java/com/apitable/workspace/service/impl/NodeRoleServiceImpl.java @@ -46,10 +46,12 @@ import com.apitable.organization.service.IMemberService; import com.apitable.organization.service.IOrganizationService; import com.apitable.organization.service.IRoleService; +import com.apitable.organization.service.ITagService; import com.apitable.organization.service.ITeamService; import com.apitable.organization.service.IUnitService; import com.apitable.organization.vo.MemberTeamPathInfo; import com.apitable.organization.vo.RoleInfoVo; +import com.apitable.organization.vo.TagInfoVo; import com.apitable.organization.vo.UnitMemberVo; import com.apitable.organization.vo.UnitTeamVo; import com.apitable.shared.cache.service.UserSpaceCacheService; @@ -144,6 +146,9 @@ public class NodeRoleServiceImpl implements INodeRoleService { @Resource private IRoleService iRoleService; + @Resource + private ITagService iTagService; + @Override @Transactional(rollbackFor = Exception.class) public void enableNodeRole(Long userId, String spaceId, String nodeId, boolean includeExtend) { @@ -413,6 +418,7 @@ public List getNodeRoleUnitList(String nodeId) { List teamIds = new ArrayList<>(); List memberIds = new ArrayList<>(); List roleIds = new ArrayList<>(); + List tagIds = new ArrayList<>(); for (ControlRoleUnitDTO nodeRoleDto : entry.getValue()) { NodeRoleUnit unit = new NodeRoleUnit(); unit.setRole(nodeRoleDto.getRole()); @@ -427,6 +433,8 @@ public List getNodeRoleUnitList(String nodeId) { memberIds.add(nodeRoleDto.getUnitRefId()); } else if (unitType == UnitType.ROLE) { roleIds.add(nodeRoleDto.getUnitRefId()); + } else if (unitType == UnitType.TAG) { + tagIds.add(nodeRoleDto.getUnitRefId()); } } // Batch query supplementary organizational unit information @@ -469,6 +477,16 @@ public List getNodeRoleUnitList(String nodeId) { roleUnits.add(unit); } } + if (!tagIds.isEmpty()) { + List tags = iTagService.getTagVos(spaceId, tagIds); + for (TagInfoVo tag : tags) { + NodeRoleUnit unit = unitIdToFieldRoleMap.get(tag.getUnitId()); + unit.setUnitRefId(tag.getTagId()); + unit.setUnitName(tag.getTagName()); + unit.setMemberCount(tag.getMemberCount()); + roleUnits.add(unit); + } + } } return roleUnits; } diff --git a/backend-server/application/src/main/java/com/apitable/workspace/service/impl/NodeServiceImpl.java b/backend-server/application/src/main/java/com/apitable/workspace/service/impl/NodeServiceImpl.java index def9102693..3e87ba987b 100644 --- a/backend-server/application/src/main/java/com/apitable/workspace/service/impl/NodeServiceImpl.java +++ b/backend-server/application/src/main/java/com/apitable/workspace/service/impl/NodeServiceImpl.java @@ -29,6 +29,7 @@ import cn.hutool.core.util.BooleanUtil; import cn.hutool.core.util.NumberUtil; import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; @@ -47,6 +48,7 @@ import com.apitable.control.infrastructure.ControlTemplate; import com.apitable.control.infrastructure.permission.NodePermission; import com.apitable.control.infrastructure.role.ControlRole; +import com.apitable.control.service.IControlService; import com.apitable.core.exception.BusinessException; import com.apitable.core.support.tree.DefaultTreeBuildFactory; import com.apitable.core.util.ExceptionUtil; @@ -76,6 +78,7 @@ import com.apitable.shared.listener.event.AuditSpaceEvent.AuditSpaceArg; import com.apitable.shared.sysconfig.i18n.I18nStringsUtil; import com.apitable.shared.util.CollectionUtil; +import com.apitable.shared.util.DBUtil; import com.apitable.shared.util.IdUtil; import com.apitable.shared.util.StringUtil; import com.apitable.shared.util.information.ClientOriginInfo; @@ -90,11 +93,13 @@ import com.apitable.template.enums.TemplateException; import com.apitable.widget.service.IWidgetService; import com.apitable.workspace.dto.CreateNodeDto; +import com.apitable.workspace.dto.DatasheetMetaColumnDTO; import com.apitable.workspace.dto.NodeBaseInfoDTO; import com.apitable.workspace.dto.NodeCopyEffectDTO; import com.apitable.workspace.dto.NodeCopyOptions; import com.apitable.workspace.dto.NodeData; import com.apitable.workspace.dto.NodeExtraDTO; +import com.apitable.workspace.dto.NodeShareDTO; import com.apitable.workspace.dto.NodeStatisticsDTO; import com.apitable.workspace.dto.NodeTreeDTO; import com.apitable.workspace.dto.UrlNodeInfoDTO; @@ -126,6 +131,7 @@ import com.apitable.workspace.service.IDatasheetService; import com.apitable.workspace.service.IFieldRoleService; import com.apitable.workspace.service.INodeDescService; +import com.apitable.workspace.service.INodeFavoriteService; import com.apitable.workspace.service.INodeRelService; import com.apitable.workspace.service.INodeRoleService; import com.apitable.workspace.service.INodeService; @@ -180,6 +186,9 @@ public class NodeServiceImpl extends ServiceImpl impleme @Resource private NodeFacade nodeFacade; + @Resource + private INodeFavoriteService iNodeFavoriteService; + @Resource private INodeDescService iNodeDescService; @@ -207,6 +216,9 @@ public class NodeServiceImpl extends ServiceImpl impleme @Resource private ControlTemplate controlTemplate; + @Resource + private IControlService iControlService; + @Resource private INodeRoleService iNodeRoleService; @@ -405,7 +417,9 @@ public List getNodeInfoByNodeIds(String spaceId, Long memberId, return new ArrayList<>(); } // Batch query node information - List infos = baseMapper.selectNodeInfoByNodeIds(roleDict.keySet(), memberId); + List infos = + this.buildNodeInfos(spaceId, roleDict.keySet(), memberId, NodeInfoVo.class); + // baseMapper.selectNodeInfoByNodeIds(roleDict.keySet(), memberId); // Node switches to memory custom sorting CollectionUtil.customSequenceSort(infos, NodeInfoVo::getNodeId, new ArrayList<>(roleDict.keySet())); @@ -414,6 +428,75 @@ public List getNodeInfoByNodeIds(String spaceId, Long memberId, return infos; } + private List buildNodeInfos(String spaceId, + Set nodeIds, + Long memberId, Class targetType) { + List nodeInfoVOs = new ArrayList<>(); + List nodes = this.getByNodeIds(nodeIds); + if (nodes.isEmpty()) { + return nodeInfoVOs; + } + + // get star node id + List favoriteNodeIds = iNodeFavoriteService.getFavoriteNodeIdsByMemberId(memberId); + // get share node id + List shareDTOList = + DBUtil.batchSelectByFieldIn(nodeIds, nodeShareSettingMapper::selectDtoByNodeIds); + List shareNodeIds = shareDTOList.stream() + .filter(s -> s.getIsEnabled().equals(Boolean.TRUE)) + .map(NodeShareDTO::getNodeId).toList(); + // get control node id + List existedControlIds = + DBUtil.batchSelectByFieldIn(nodeIds, iControlService::getExistedControlId); + + // query folder ids with child nodes + List folderIdsWithChildren = new ArrayList<>(); + List folderNodeIds = nodes.stream() + .filter(n -> NodeType.toEnum(n.getType()).isFolder()) + .map(NodeEntity::getNodeId).toList(); + if (folderNodeIds.size() > 0) { + folderIdsWithChildren = + DBUtil.batchSelectByFieldIn(folderNodeIds, baseMapper::selectParentIdWithChildren); + } + // query datasheet column count + Map dstIdToColumnCountMap = new HashMap<>(); + List datasheetNodeIds = nodes.stream() + .filter(n -> n.getType().equals(NodeType.DATASHEET.getNodeType())) + .map(NodeEntity::getNodeId).toList(); + if (datasheetNodeIds.size() > 0) { + List metaColumnDTOs = + iDatasheetMetaService.findMetaColumnDTOs(datasheetNodeIds); + dstIdToColumnCountMap = metaColumnDTOs.stream().collect( + Collectors.toMap(DatasheetMetaColumnDTO::getDstId, + DatasheetMetaColumnDTO::getMdFieldMapSize)); + } + + for (NodeEntity node : nodes) { + String nodeId = node.getNodeId(); + T nodeInfo = ReflectUtil.newInstanceIfPossible(targetType); + BeanUtil.copyProperties(node, nodeInfo); + nodeInfo.setNodeFavorite(favoriteNodeIds.contains(nodeId)); + nodeInfo.setNodeShared(shareNodeIds.contains(nodeId)); + nodeInfo.setNodePermitSet(existedControlIds.contains(nodeId)); + nodeInfo.setNodePrivate(!node.getUnitId().equals(0L)); + + NodeType nodeType = NodeType.toEnum(node.getType()); + if (nodeType.isRoot()) { + // replace node name if type is root node + String spaceName = iSpaceService.getNameBySpaceId(spaceId); + nodeInfo.setNodeName(spaceName); + nodeInfo.setHasChildren(nodes.size() > 1); + } else if (nodeType.isFolder()) { + nodeInfo.setHasChildren(folderIdsWithChildren.contains(nodeId)); + } else if (nodeType.equals(NodeType.DATASHEET)) { + nodeInfo.setMdFieldMapSize(dstIdToColumnCountMap.get(nodeId)); + } + + nodeInfoVOs.add(nodeInfo); + } + return nodeInfoVOs; + } + @Override public String checkNodeIfExist(String spaceId, String nodeId) { log.info("Check if the node exists"); @@ -695,11 +778,13 @@ public NodeInfoTreeVo getNodeInfoTreeByNodeIds(String spaceId, Long memberId, ControlRoleDict roleDict = controlTemplate.fetchNodeTreeNode(memberId, nodeIds); ExceptionUtil.isFalse(roleDict.isEmpty(), PermissionException.NODE_ACCESS_DENIED); List treeList = - CollUtil.split(roleDict.keySet(), 1000).stream() + CollUtil.split(roleDict.keySet(), 1).stream() .reduce(new ArrayList<>(), (nodes, item) -> { List childNodes = - baseMapper.selectNodeInfoTreeByNodeIds(item, memberId); + this.buildNodeInfos(spaceId, new HashSet<>(item), memberId, + NodeInfoTreeVo.class); + // baseMapper.selectNodeInfoTreeByNodeIds(item, memberId); nodes.addAll(childNodes); return nodes; }, diff --git a/backend-server/application/src/main/resources/application.yml b/backend-server/application/src/main/resources/application.yml index 05d8097426..de0c58e163 100644 --- a/backend-server/application/src/main/resources/application.yml +++ b/backend-server/application/src/main/resources/application.yml @@ -91,6 +91,7 @@ limit: template-max-count: ${TEMPLATE_MAX_COUNT:20} dsb-widget-max-count: ${DSB_WIDGET_MAX_COUNT:30} dst-robot-max-count: ${DST_ROBOT_MAX_COUNT:10} + max-invite-count-for-free: ${MAX_INVITE_COUNT_FOR_FREE:10} socket: domain: ${SOCKET_DOMAIN:http://127.0.0.1:3333/socket} @@ -202,3 +203,7 @@ system: databus: client: host: ${DATABUS_SERVER_BASE_URL:http://127.0.0.1:8625} + +notification: + callback-url: ${NOTIFICATION_CALLBACK_URL:} + template-ids: ${NOTIFICATION_TEMPLATE_IDS:single_record_member_mention,single_record_comment_mentioned,task_reminder} diff --git a/backend-server/application/src/main/resources/mapper/organization/TagMapper.xml b/backend-server/application/src/main/resources/mapper/organization/TagMapper.xml new file mode 100644 index 0000000000..c6239505d2 --- /dev/null +++ b/backend-server/application/src/main/resources/mapper/organization/TagMapper.xml @@ -0,0 +1,44 @@ + + + + + + + + UPDATE ${tablePrefix}unit_tag + SET tag_name = #{name} + WHERE id = #{tagId} + + + + + \ No newline at end of file diff --git a/backend-server/application/src/main/resources/mapper/organization/TagMemberRelMapper.xml b/backend-server/application/src/main/resources/mapper/organization/TagMemberRelMapper.xml new file mode 100644 index 0000000000..6a5af2a5f1 --- /dev/null +++ b/backend-server/application/src/main/resources/mapper/organization/TagMemberRelMapper.xml @@ -0,0 +1,76 @@ + + + + + + + + INSERT INTO ${tablePrefix}unit_tag_member_rel(id, tag_id, member_id) + VALUES + + + #{item.id},#{item.tagId}, #{item.memberId} + + + + + + + + + + + DELETE FROM ${tablePrefix}unit_tag_member_rel vodmr + WHERE vodmr.tag_id = #{tagId} + AND vodmr.member_id in + + #{item} + + + + + DELETE FROM ${tablePrefix}unit_tag_member_rel vodmr + WHERE vodmr.tag_id = #{tagId} + + + + + + \ No newline at end of file diff --git a/backend-server/application/src/main/resources/mapper/workspace/DatasheetMetaMapper.xml b/backend-server/application/src/main/resources/mapper/workspace/DatasheetMetaMapper.xml index f17091fd39..6af060624f 100755 --- a/backend-server/application/src/main/resources/mapper/workspace/DatasheetMetaMapper.xml +++ b/backend-server/application/src/main/resources/mapper/workspace/DatasheetMetaMapper.xml @@ -40,6 +40,16 @@ WHERE dst_id = #{dstId} AND is_deleted = 0 + + + + { return ( } - href={'javascript:void(0)'} + href={'#'} underline={false} color={colors.secondLevelText} onClick={onClick} diff --git a/packaging/Dockerfile.airagent-web b/packaging/Dockerfile.airagent-web index ed0625bc39..2a40b0c5be 100644 --- a/packaging/Dockerfile.airagent-web +++ b/packaging/Dockerfile.airagent-web @@ -1,11 +1,12 @@ # Install dependencies only when needed FROM apitable/nodepy:16.15.0-alpine AS deps -RUN npm install -g pnpm +RUN npm install -g pnpm@8.13.1 WORKDIR /workspace-install COPY ./pnpm-workspace.yaml ./package.json ./pnpm-lock.yaml ./ +COPY ./patches/ ./patches # air-agent dependencies packages COPY packages/components/package.json ./packages/components/ @@ -29,7 +30,7 @@ ARG NEXT_ASSET_PREFIX="" ARG NEXT_PUBLIC_ASSET_PREFIX="" ENV SEMVER_FULL=${SEMVER_FULL} -RUN npm install -g pnpm +RUN npm install -g pnpm@8.13.1 WORKDIR /app diff --git a/packaging/Dockerfile.storybook b/packaging/Dockerfile.storybook index 4e2964a586..93951f2eff 100644 --- a/packaging/Dockerfile.storybook +++ b/packaging/Dockerfile.storybook @@ -4,6 +4,7 @@ FROM apitable/nodepy:16.15.0-alpine AS deps WORKDIR /workspace-install COPY ./pnpm-workspace.yaml ./package.json ./pnpm-lock.yaml ./ +COPY ./patches/ ./patches # components COPY packages/components/package.json ./packages/components/ @@ -15,6 +16,7 @@ FROM apitable/nodepy:16.15.0-alpine AS builder WORKDIR /app +COPY --from=deps /workspace-install/patches ./patches COPY --from=deps /workspace-install/node_modules ./node_modules COPY --from=deps /workspace-install/packages/components/node_modules ./packages/components/node_modules diff --git a/packaging/Dockerfile.web-server b/packaging/Dockerfile.web-server index f7a8bd5885..f65751862f 100644 --- a/packaging/Dockerfile.web-server +++ b/packaging/Dockerfile.web-server @@ -1,11 +1,12 @@ # Install dependencies only when needed FROM apitable/nodepy:16.15.0-alpine AS deps -RUN npm install -g pnpm +RUN npm install -g pnpm@8.13.1 WORKDIR /workspace-install COPY ./pnpm-workspace.yaml ./package.json ./pnpm-lock.yaml ./ +COPY ./patches/ ./patches # datasheet dependencies packages COPY packages/components/package.json ./packages/components/ @@ -31,13 +32,14 @@ ARG NEXT_ASSET_PREFIX="" ARG NEXT_PUBLIC_ASSET_PREFIX="" ENV SEMVER_FULL=${SEMVER_FULL} -RUN npm install -g pnpm +RUN npm install -g pnpm@8.13.1 WORKDIR /app COPY ./pnpm-workspace.yaml ./build.js ./package.json ./pnpm-lock.yaml ./nx.json ./common-tsconfig.json ./tsconfig.json ./.eslintrc ./ COPY packages/ ./packages/ +COPY --from=deps /workspace-install/patches ./patches COPY --from=deps /workspace-install/node_modules ./node_modules COPY --from=deps /workspace-install/packages/datasheet/node_modules ./packages/datasheet/node_modules COPY --from=deps /workspace-install/packages/components/node_modules ./packages/components/node_modules diff --git a/packaging/all-in-one/all-in-one/script/init-appdata.sh b/packaging/all-in-one/all-in-one/script/init-appdata.sh index 3e1a56c1b3..dd9c2bdd4c 100755 --- a/packaging/all-in-one/all-in-one/script/init-appdata.sh +++ b/packaging/all-in-one/all-in-one/script/init-appdata.sh @@ -27,7 +27,10 @@ liquibase \ --database-changelog-lock-table-name="${DATABASE_TABLE_PREFIX:=apitable_}db_changelog_lock" \ --url="jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_DATABASE}?characterEncoding=utf8&autoReconnect=true&useSSL=true" \ update \ - -Dtable.prefix="${DATABASE_TABLE_PREFIX:=apitable_}" + -Dtable.prefix="${DATABASE_TABLE_PREFIX:=apitable_} \ + -DDB_ENGINE=${DB_ENGINE:=mysql}" + +cd /app/init-appdata for action in init-user load; do java -jar /app/init-appdata/init-appdata.jar "${action}" diff --git a/patches/mysql2@2.3.3.patch b/patches/mysql2@3.9.7.patch similarity index 100% rename from patches/mysql2@2.3.3.patch rename to patches/mysql2@3.9.7.patch diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 588a62fb69..98d89ff353 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,9 +11,9 @@ overrides: packageExtensionsChecksum: 49a5c42ebdc331495663a344302e74b8 patchedDependencies: - mysql2@2.3.3: + mysql2@3.9.7: hash: 42lfj6petuckj7k47ered7iyhi - path: patches/mysql2@2.3.3.patch + path: patches/mysql2@3.9.7.patch importers: @@ -434,7 +434,7 @@ importers: version: 8.0.4(@types/react-dom@18.2.7)(@types/react@18.0.2)(react-dom@18.2.0)(react@18.2.0)(redux@4.2.0) sass: specifier: ^1.68.0 - version: 1.69.5 + version: 1.77.0 store2: specifier: ^2.12.0 version: 2.14.2 @@ -621,7 +621,7 @@ importers: version: 1.0.1 next: specifier: ^12.3.1 - version: 12.3.4(@babel/core@7.22.20)(react-dom@18.2.0)(react@18.2.0)(sass@1.69.5) + version: 12.3.4(@babel/core@7.22.20)(react-dom@18.2.0)(react@18.2.0)(sass@1.77.0) next-compose-plugins: specifier: ^2.2.1 version: 2.2.1 @@ -1366,6 +1366,9 @@ importers: dom-to-image: specifier: ^2.6.0 version: 2.6.0 + dompurify: + specifier: ^3.0.1 + version: 3.0.5 dot-object: specifier: ^2.1.2 version: 2.1.4 @@ -1742,6 +1745,9 @@ importers: '@types/dom-to-image': specifier: ^2.6.2 version: 2.6.4 + '@types/dompurify': + specifier: ^3 + version: 3.0.2 '@types/dot-object': specifier: ^2.1.2 version: 2.1.2 @@ -1906,7 +1912,7 @@ importers: version: 4.1.3 next: specifier: ^12.3.1 - version: 12.3.4(@babel/core@7.22.20)(react-dom@18.2.0)(react@18.2.0)(sass@1.69.5) + version: 12.3.4(@babel/core@7.22.20)(react-dom@18.2.0)(react@18.2.0)(sass@1.77.0) next-compose-plugins: specifier: ^2.2.1 version: 2.2.1 @@ -2242,8 +2248,8 @@ importers: specifier: ^7.0.26 version: 7.1.3 mysql2: - specifier: ^2.3.2 - version: 2.3.3(patch_hash=42lfj6petuckj7k47ered7iyhi) + specifier: ^3.9.7 + version: 3.9.7(patch_hash=42lfj6petuckj7k47ered7iyhi) nest-winston: specifier: ^1.8.0 version: 1.9.4(@nestjs/common@8.1.2)(winston@3.10.0) @@ -2306,7 +2312,7 @@ importers: version: 1.3.1 typeorm: specifier: ^0.2.38 - version: 0.2.45(ioredis@5.3.2)(mysql2@2.3.3) + version: 0.2.45(ioredis@5.3.2)(mysql2@3.9.7) winston: specifier: ^3.8.2 version: 3.10.0 @@ -3775,7 +3781,7 @@ packages: gensync: 1.0.0-beta.2 json5: 2.2.3 lodash: 4.17.21 - resolve: 1.22.4 + resolve: 1.22.8 semver: 5.7.2 source-map: 0.5.7 transitivePeerDependencies: @@ -3868,7 +3874,7 @@ packages: dependencies: '@babel/compat-data': 7.22.9 '@babel/helper-validator-option': 7.22.15 - browserslist: 4.21.10 + browserslist: 4.22.1 lru-cache: 5.1.1 semver: 6.3.1 @@ -3944,7 +3950,7 @@ packages: '@babel/traverse': 7.22.20 debug: 4.3.4(supports-color@8.1.1) lodash.debounce: 4.0.8 - resolve: 1.22.4 + resolve: 1.22.8 semver: 6.3.1 transitivePeerDependencies: - supports-color @@ -3960,7 +3966,7 @@ packages: '@babel/helper-plugin-utils': 7.22.5 debug: 4.3.4(supports-color@8.1.1) lodash.debounce: 4.0.8 - resolve: 1.22.4 + resolve: 1.22.8 transitivePeerDependencies: - supports-color dev: false @@ -3975,7 +3981,7 @@ packages: '@babel/helper-plugin-utils': 7.22.5 debug: 4.3.4(supports-color@8.1.1) lodash.debounce: 4.0.8 - resolve: 1.22.4 + resolve: 1.22.8 transitivePeerDependencies: - supports-color dev: true @@ -7859,7 +7865,7 @@ packages: '@bcoe/v8-coverage': 0.2.3 '@jest/console': 29.6.4 '@jest/test-result': 29.6.4 - '@jest/transform': 29.6.4 + '@jest/transform': 29.7.0 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.19 '@types/node': 14.18.58 @@ -7875,7 +7881,7 @@ packages: istanbul-reports: 3.1.6 jest-message-util: 29.6.3 jest-util: 29.6.3 - jest-worker: 29.6.4 + jest-worker: 29.7.0 slash: 3.0.0 string-length: 4.0.2 strip-ansi: 6.0.1 @@ -7998,7 +8004,7 @@ packages: dependencies: '@jest/test-result': 29.6.4 graceful-fs: 4.2.11 - jest-haste-map: 29.6.4 + jest-haste-map: 29.7.0 slash: 3.0.0 dev: true @@ -8232,7 +8238,7 @@ packages: resolution: {integrity: sha512-jI48mSnRgFQxXiE/UTUCVCpX8lK3wCFKLF1Ss2aEreboKNuLQGt3e0/YFqWVHe/WENxOaqiJvwOz+L/SrN2+qQ==} hasBin: true dependencies: - '@types/json-schema': 7.0.12 + '@types/json-schema': 7.0.15 '@types/node': 16.18.48 glob: 7.2.3 path-equal: 1.2.5 @@ -8843,7 +8849,7 @@ packages: '@nestjs/core': 8.1.2(@nestjs/common@8.1.2)(@nestjs/microservices@8.1.2)(@nestjs/websockets@8.1.2)(reflect-metadata@0.1.13)(rxjs@7.5.7) reflect-metadata: 0.1.13 rxjs: 7.5.7 - typeorm: 0.2.45(ioredis@5.3.2)(mysql2@2.3.3) + typeorm: 0.2.45(ioredis@5.3.2)(mysql2@3.9.7) uuid: 8.3.2 dev: false @@ -10425,7 +10431,7 @@ packages: '@sentry/vercel-edge': 7.92.0 '@sentry/webpack-plugin': 1.21.0 chalk: 3.0.0 - next: 12.3.4(@babel/core@7.22.20)(react-dom@18.2.0)(react@18.2.0)(sass@1.69.5) + next: 12.3.4(@babel/core@7.22.20)(react-dom@18.2.0)(react@18.2.0)(sass@1.77.0) react: 18.2.0 resolve: 1.22.8 rollup: 2.78.0 @@ -13288,7 +13294,7 @@ packages: resolution: {integrity: sha512-sdPRb9K6iL5XZOmBubg8yiFp5yS/JdUDQsq5e6h95km91MCYMuvp7mh1fjPEYUhvHepKpZOjnEaMBR4PxjWDzg==} dependencies: '@types/estree': 1.0.1 - '@types/json-schema': 7.0.12 + '@types/json-schema': 7.0.15 /@types/estree@0.0.50: resolution: {integrity: sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==} @@ -13452,7 +13458,7 @@ packages: resolution: {integrity: sha512-vx1CRDeDUwQ0Pc7v+hS61O1ETA81kD04IMEC0hS1kPyVtHDdZrokAvpF7MT9VI/fVSzicelUZNCepDvhRV1PeA==} deprecated: This is a stub types definition. jest-diff provides its own type definitions, so you do not need this installed. dependencies: - jest-diff: 29.6.4 + jest-diff: 29.7.0 dev: true /@types/jest@24.0.18: @@ -13518,7 +13524,6 @@ packages: /@types/json-schema@7.0.15: resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - dev: false /@types/json5@0.0.29: resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} @@ -14419,7 +14424,7 @@ packages: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.49.0) - '@types/json-schema': 7.0.12 + '@types/json-schema': 7.0.15 '@types/semver': 7.5.1 '@typescript-eslint/scope-manager': 5.62.0 '@typescript-eslint/types': 5.62.0 @@ -15868,8 +15873,8 @@ packages: peerDependencies: postcss: ^8.1.0 dependencies: - browserslist: 4.22.1 - caniuse-lite: 1.0.30001563 + browserslist: 4.21.10 + caniuse-lite: 1.0.30001528 fraction.js: 4.3.6 normalize-range: 0.1.2 picocolors: 1.0.0 @@ -16083,24 +16088,6 @@ packages: - supports-color dev: true - /babel-jest@29.6.4(@babel/core@7.22.20): - resolution: {integrity: sha512-meLj23UlSLddj6PC+YTOFRgDAtjnZom8w/ACsrx0gtPtv5cJZk0A5Unk5bV4wixD7XaPCN1fQvpww8czkZURmw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@babel/core': ^7.8.0 - dependencies: - '@babel/core': 7.22.20 - '@jest/transform': 29.6.4 - '@types/babel__core': 7.20.1 - babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 29.6.3(@babel/core@7.22.20) - chalk: 4.1.2 - graceful-fs: 4.2.11 - slash: 3.0.0 - transitivePeerDependencies: - - supports-color - dev: true - /babel-jest@29.7.0(@babel/core@7.22.20): resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -16257,7 +16244,7 @@ packages: dependencies: '@babel/runtime': 7.23.2 cosmiconfig: 6.0.0 - resolve: 1.22.4 + resolve: 1.22.8 dev: true /babel-plugin-macros@3.1.0: @@ -16744,7 +16731,7 @@ packages: hasBin: true dependencies: quote-stream: 1.0.2 - resolve: 1.22.4 + resolve: 1.22.8 static-module: 3.0.4 through2: 2.0.5 dev: true @@ -16777,7 +16764,7 @@ packages: /browser-resolve@2.0.0: resolution: {integrity: sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ==} dependencies: - resolve: 1.22.4 + resolve: 1.22.8 /browserify-aes@1.2.0: resolution: {integrity: sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==} @@ -16953,10 +16940,11 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001528 + caniuse-lite: 1.0.30001563 electron-to-chromium: 1.4.510 node-releases: 2.0.13 update-browserslist-db: 1.0.11(browserslist@4.21.10) + dev: true /browserslist@4.22.1: resolution: {integrity: sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==} @@ -17299,8 +17287,8 @@ packages: /caniuse-api@3.0.0: resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} dependencies: - browserslist: 4.21.10 - caniuse-lite: 1.0.30001528 + browserslist: 4.22.1 + caniuse-lite: 1.0.30001563 lodash.memoize: 4.1.2 lodash.uniq: 4.5.0 dev: true @@ -18362,7 +18350,7 @@ packages: /core-js-compat@3.32.1: resolution: {integrity: sha512-GSvKDv4wE0bPnQtjklV101juQ85g6H3rm5PDP20mqlS5j0kXF3pP97YvAu5hl+uFHqMictp3b2VxOHljWMAtuA==} dependencies: - browserslist: 4.21.10 + browserslist: 4.22.1 /core-js-pure@3.32.1: resolution: {integrity: sha512-f52QZwkFVDPf7UEQZGHKx6NYxsxmVGJe5DIvbzOdRMJlmT6yv0KDjR8rmy3ngr/t5wU54c7Sp/qIJH0ppbhVpQ==} @@ -20028,6 +20016,7 @@ packages: /electron-to-chromium@1.4.510: resolution: {integrity: sha512-xPfLIPFcN/WLXBpQ/K4UgE98oUBO5Tia6BD4rkSR0wE7ep/PwBVlgvPJQrIBpmJGVAmUzwPKuDbVt9XV6+uC2g==} + dev: true /electron-to-chromium@1.4.590: resolution: {integrity: sha512-hohItzsQcG7/FBsviCYMtQwUSWvVF7NVqPOnJCErWsAshsP/CR2LAXdmq276RbESNdhxiAq5/vRo1g2pxGXVww==} @@ -22105,7 +22094,7 @@ packages: optional: true dependencies: '@babel/code-frame': 7.22.13 - '@types/json-schema': 7.0.12 + '@types/json-schema': 7.0.15 chalk: 4.1.2 chokidar: 3.5.3 cosmiconfig: 6.0.0 @@ -24546,7 +24535,7 @@ packages: jest-message-util: 29.6.3 jest-runtime: 29.6.4 jest-snapshot: 29.6.4 - jest-util: 29.6.3 + jest-util: 29.7.0 p-limit: 3.1.0 pretty-format: 29.6.3 pure-rand: 6.0.3 @@ -24756,7 +24745,7 @@ packages: '@jest/test-sequencer': 29.6.4 '@jest/types': 29.6.3 '@types/node': 14.18.58 - babel-jest: 29.6.4(@babel/core@7.22.20) + babel-jest: 29.7.0(@babel/core@7.22.20) chalk: 4.1.2 ci-info: 3.8.0 deepmerge: 4.3.1 @@ -24797,7 +24786,7 @@ packages: '@jest/test-sequencer': 29.6.4 '@jest/types': 29.6.3 '@types/node': 20.6.2 - babel-jest: 29.6.4(@babel/core@7.22.20) + babel-jest: 29.7.0(@babel/core@7.22.20) chalk: 4.1.2 ci-info: 3.8.0 deepmerge: 4.3.1 @@ -24941,8 +24930,8 @@ packages: '@jest/types': 29.6.3 chalk: 4.1.2 jest-get-type: 29.6.3 - jest-util: 29.6.3 - pretty-format: 29.6.3 + jest-util: 29.7.0 + pretty-format: 29.7.0 dev: true /jest-each@29.7.0: @@ -25042,7 +25031,7 @@ packages: '@jest/types': 29.6.3 '@types/node': 14.18.58 jest-mock: 29.6.3 - jest-util: 29.6.3 + jest-util: 29.7.0 dev: true /jest-environment-node@29.7.0: @@ -25125,7 +25114,7 @@ packages: graceful-fs: 4.2.11 jest-regex-util: 29.6.3 jest-util: 29.6.3 - jest-worker: 29.6.4 + jest-worker: 29.7.0 micromatch: 4.0.5 walker: 1.0.8 optionalDependencies: @@ -25365,7 +25354,7 @@ packages: jest-pnp-resolver: 1.2.3(jest-resolve@28.1.3) jest-util: 28.1.3 jest-validate: 28.1.3 - resolve: 1.22.4 + resolve: 1.22.8 resolve.exports: 1.1.1 slash: 3.0.0 dev: true @@ -25380,7 +25369,7 @@ packages: jest-pnp-resolver: 1.2.3(jest-resolve@29.6.4) jest-util: 29.6.3 jest-validate: 29.6.3 - resolve: 1.22.4 + resolve: 1.22.8 resolve.exports: 2.0.2 slash: 3.0.0 dev: true @@ -25395,7 +25384,7 @@ packages: jest-pnp-resolver: 1.2.3(jest-resolve@29.7.0) jest-util: 29.7.0 jest-validate: 29.7.0 - resolve: 1.22.4 + resolve: 1.22.8 resolve.exports: 2.0.2 slash: 3.0.0 dev: true @@ -25436,7 +25425,7 @@ packages: '@jest/console': 29.6.4 '@jest/environment': 29.6.4 '@jest/test-result': 29.6.4 - '@jest/transform': 29.6.4 + '@jest/transform': 29.7.0 '@jest/types': 29.6.3 '@types/node': 14.18.58 chalk: 4.1.2 @@ -25451,7 +25440,7 @@ packages: jest-runtime: 29.6.4 jest-util: 29.6.3 jest-watcher: 29.6.4 - jest-worker: 29.6.4 + jest-worker: 29.7.0 p-limit: 3.1.0 source-map-support: 0.5.13 transitivePeerDependencies: @@ -25526,7 +25515,7 @@ packages: '@jest/globals': 29.6.4 '@jest/source-map': 29.6.3 '@jest/test-result': 29.6.4 - '@jest/transform': 29.6.4 + '@jest/transform': 29.7.0 '@jest/types': 29.6.3 '@types/node': 14.18.58 chalk: 4.1.2 @@ -25626,7 +25615,7 @@ packages: '@babel/plugin-syntax-typescript': 7.22.5(@babel/core@7.22.20) '@babel/types': 7.22.19 '@jest/expect-utils': 29.6.4 - '@jest/transform': 29.6.4 + '@jest/transform': 29.7.0 '@jest/types': 29.6.3 babel-preset-current-node-syntax: 1.0.1(@babel/core@7.22.20) chalk: 4.1.2 @@ -25823,16 +25812,6 @@ packages: supports-color: 8.1.1 dev: true - /jest-worker@29.6.4: - resolution: {integrity: sha512-6dpvFV4WjcWbDVGgHTWo/aupl8/LbBx2NSKfiwqf79xC/yeJjKHT1+StcKy/2KTmW16hE68ccKVOtXf+WZGz7Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@types/node': 14.18.58 - jest-util: 29.6.3 - merge-stream: 2.0.0 - supports-color: 8.1.1 - dev: true - /jest-worker@29.7.0: resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -26992,6 +26971,7 @@ packages: /long@4.0.0: resolution: {integrity: sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==} + dev: true /long@5.2.3: resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==} @@ -27065,6 +27045,11 @@ packages: engines: {node: '>=12'} dev: false + /lru-cache@8.0.5: + resolution: {integrity: sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==} + engines: {node: '>=16.14'} + dev: false + /lru-queue@0.1.0: resolution: {integrity: sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==} dependencies: @@ -27804,7 +27789,7 @@ packages: inherits: 2.0.4 parents: 1.0.1 readable-stream: 2.3.8 - resolve: 1.22.4 + resolve: 1.22.8 stream-combiner2: 1.1.1 subarg: 1.0.0 through2: 2.0.5 @@ -27958,15 +27943,15 @@ packages: engines: {node: '>=12.0.0'} dev: true - /mysql2@2.3.3(patch_hash=42lfj6petuckj7k47ered7iyhi): - resolution: {integrity: sha512-wxJUev6LgMSgACDkb/InIFxDprRa6T95+VEoR+xPvtngtccNH2dGjEB/fVZ8yg1gWv1510c9CvXuJHi5zUm0ZA==} + /mysql2@3.9.7(patch_hash=42lfj6petuckj7k47ered7iyhi): + resolution: {integrity: sha512-KnJT8vYRcNAZv73uf9zpXqNbvBG7DJrs+1nACsjZP1HMJ1TgXEy8wnNilXAn/5i57JizXKtrUtwDB7HxT9DDpw==} engines: {node: '>= 8.0'} dependencies: denque: 2.1.0 generate-function: 2.3.1 iconv-lite: 0.6.3 - long: 4.0.0 - lru-cache: 6.0.0 + long: 5.2.3 + lru-cache: 8.0.5 named-placeholders: 1.1.3 seq-queue: 0.0.5 sqlstring: 2.3.3 @@ -28226,10 +28211,10 @@ packages: clone-deep: 4.0.1 less: 4.1.3 less-loader: 11.0.0(less@4.1.3)(webpack@5.89.0) - next: 12.3.4(@babel/core@7.22.20)(react-dom@18.2.0)(react@18.2.0)(sass@1.69.5) + next: 12.3.4(@babel/core@7.22.20)(react-dom@18.2.0)(react@18.2.0)(sass@1.77.0) dev: false - /next@12.3.4(@babel/core@7.22.20)(react-dom@18.2.0)(react@18.2.0)(sass@1.69.5): + /next@12.3.4(@babel/core@7.22.20)(react-dom@18.2.0)(react@18.2.0)(sass@1.77.0): resolution: {integrity: sha512-VcyMJUtLZBGzLKo3oMxrEF0stxh8HwuW976pAzlHhI3t8qJ4SROjCrSh1T24bhrbjw55wfZXAbXPGwPt5FLRfQ==} engines: {node: '>=12.22.0'} hasBin: true @@ -28253,7 +28238,7 @@ packages: postcss: 8.4.14 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - sass: 1.69.5 + sass: 1.77.0 styled-jsx: 5.0.7(@babel/core@7.22.20)(react@18.2.0) use-sync-external-store: 1.2.0(react@18.2.0) optionalDependencies: @@ -28469,7 +28454,7 @@ packages: resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} dependencies: hosted-git-info: 2.8.9 - resolve: 1.22.4 + resolve: 1.22.8 semver: 5.7.2 validate-npm-package-license: 3.0.4 dev: true @@ -29847,7 +29832,7 @@ packages: postcss: 8.4.31 postcss-value-parser: 4.2.0 read-cache: 1.0.0 - resolve: 1.22.4 + resolve: 1.22.8 dev: true /postcss-import@15.1.0(postcss@8.4.29): @@ -29991,7 +29976,7 @@ packages: peerDependencies: postcss: ^8.2.15 dependencies: - browserslist: 4.21.10 + browserslist: 4.22.1 cssnano-utils: 3.1.0(postcss@8.4.31) postcss: 8.4.31 postcss-value-parser: 4.2.0 @@ -30170,7 +30155,7 @@ packages: peerDependencies: postcss: ^8.2.15 dependencies: - browserslist: 4.21.10 + browserslist: 4.22.1 postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: true @@ -32824,7 +32809,7 @@ packages: resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==} engines: {node: '>= 0.10'} dependencies: - resolve: 1.22.4 + resolve: 1.22.8 dev: true /recursive-readdir@2.2.2: @@ -33235,7 +33220,6 @@ packages: is-core-module: 2.13.0 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - dev: false /resolve@2.0.0-next.4: resolution: {integrity: sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==} @@ -33496,8 +33480,8 @@ packages: - supports-color dev: true - /sass@1.69.5: - resolution: {integrity: sha512-qg2+UCJibLr2LCVOt3OlPhr/dqVHWOa9XtZf2OjbLs/T4VPSJ00udtgJxH3neXZm+QqX8B+3cU7RaLqp1iVfcQ==} + /sass@1.77.0: + resolution: {integrity: sha512-eGj4HNfXqBWtSnvItNkn7B6icqH14i3CiCGbzMKs3BAPTq62pp9NBYsBgyN4cA+qssqo9r26lW4JSvlaUUWbgw==} engines: {node: '>=14.0.0'} hasBin: true dependencies: @@ -33539,7 +33523,7 @@ packages: resolution: {integrity: sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==} engines: {node: '>= 8.9.0'} dependencies: - '@types/json-schema': 7.0.12 + '@types/json-schema': 7.0.15 ajv: 6.12.6 ajv-keywords: 3.5.2(ajv@6.12.6) dev: true @@ -33557,7 +33541,7 @@ packages: resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} engines: {node: '>= 10.13.0'} dependencies: - '@types/json-schema': 7.0.12 + '@types/json-schema': 7.0.15 ajv: 6.12.6 ajv-keywords: 3.5.2(ajv@6.12.6) @@ -33565,7 +33549,7 @@ packages: resolution: {integrity: sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==} engines: {node: '>= 12.13.0'} dependencies: - '@types/json-schema': 7.0.12 + '@types/json-schema': 7.0.15 ajv: 8.12.0 ajv-formats: 2.1.1(ajv@8.12.0) ajv-keywords: 5.1.0(ajv@8.12.0) @@ -34960,7 +34944,7 @@ packages: peerDependencies: postcss: ^8.2.15 dependencies: - browserslist: 4.21.10 + browserslist: 4.22.1 postcss: 8.4.31 postcss-selector-parser: 6.0.13 dev: true @@ -36309,7 +36293,7 @@ packages: typescript: 4.8.2 dev: true - /typeorm@0.2.45(ioredis@5.3.2)(mysql2@2.3.3): + /typeorm@0.2.45(ioredis@5.3.2)(mysql2@3.9.7): resolution: {integrity: sha512-c0rCO8VMJ3ER7JQ73xfk0zDnVv0WDjpsP6Q1m6CVKul7DB9iVdWLRjPzc8v2eaeBuomsbZ2+gTaYr8k1gm3bYA==} hasBin: true peerDependencies: @@ -36371,7 +36355,7 @@ packages: ioredis: 5.3.2 js-yaml: 4.1.0 mkdirp: 1.0.4 - mysql2: 2.3.3(patch_hash=42lfj6petuckj7k47ered7iyhi) + mysql2: 3.9.7(patch_hash=42lfj6petuckj7k47ered7iyhi) reflect-metadata: 0.1.13 sha.js: 2.4.11 tslib: 2.6.2 @@ -36695,6 +36679,7 @@ packages: browserslist: 4.21.10 escalade: 3.1.1 picocolors: 1.0.0 + dev: true /update-browserslist-db@1.0.13(browserslist@4.22.1): resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==}