committed by
GitHub
508 changed files with 32119 additions and 3465 deletions
@ -0,0 +1,76 @@ |
|||
# 10.2.0 - 10.4.0 |
|||
|
|||
## 模块变更 |
|||
|
|||
1. [身份标识模块] 增加不活跃用户通知/清理作业,实现了三个通知模板(账户保持活跃通知、账户停用通知、账户删除通知) |
|||
2. [身份标识模块] 增加不活跃用户实体 `IdentityUserInactive` 需要执行数据库迁移 |
|||
3. [身份标识模块] 增加用户登录Url设置定义: `Abp.Identity.Link.UserLoginUri`, 用于在外部系统中关联用户登录页 |
|||
4. [身份认证模块] 增加用户登录Url定义: `Abp.Account.UserLogin` |
|||
5. [身份认证模块] 增加关联用户登录功能, 可以实现跨租户用户关联, 切换关联用户登录 |
|||
6. [设置管理模块] 增加身份标识模块缺少的设置项: 启用防止密码重用、防止密码重用数量 |
|||
7. [设置基础模块] 增加设置定义分组、所属设置、优先级、依赖权限、依赖功能、选择项等常用结构扩展 |
|||
8. [设置管理模块] 重构设置管理应用服务接口, 通过预设定的设置定义分组、上下级关系等自动构成Dto结构 |
|||
|
|||
## 依赖项变更 |
|||
|
|||
| 库名称 | 原版本 | 现版本 | |
|||
| -------------------------------------------- | --------- | ------ | |
|||
| Microsoft.AspNetCore.* | 10.0.2 | 10.0.7 | |
|||
| Microsoft.Bcl.AsyncInterfaces | 10.0.2 | 10.0.7 | |
|||
| Microsoft.Data.Sqlite.Core | 10.0.0 | 10.0.7 | |
|||
| Microsoft.EntityFrameworkCore.* | 10.0.2 | 10.0.7 | |
|||
| Microting.EntityFrameworkCore.MySql | 10.0.3 | 10.0.7 | |
|||
| Microsoft.Extensions.* | 10.0.2 | 10.0.7 | |
|||
| OpenTelemetry.Extensions.Hosting | 1.14.0 | 1.15.3 | |
|||
| OpenTelemetry.Exporter.Console | 1.14.0 | 1.15.3 | |
|||
| OpenTelemetry.Exporter.OpenTelemetryProtocol | 1.14.0 | 1.15.3 | |
|||
| OpenTelemetry.Exporter.Zipkin | 1.14.0 | 1.15.3 | |
|||
| OpenTelemetry.Instrumentation.AspNetCore | 1.14.0 | 1.15.2 | |
|||
| OpenTelemetry.Instrumentation.Http | 1.14.0 | 1.15.1 | |
|||
| OpenTelemetry.Instrumentation.Quartz | 1.14.0-beta.2 | 1.15.1-beta.1 | |
|||
| OpenTelemetry.Instrumentation.Runtime | 1.14.0 | 1.15.1 | |
|||
| OpenTelemetry.Instrumentation.SqlClient | 1.14.0-beta.1 | 1.15.2 | |
|||
| OpenIddict.Server.DataProtection | 7.2.0 | 7.5.0 | |
|||
| OpenIddict.Validation.DataProtection | 7.2.0 | 7.5.0 | |
|||
| System.Threading.Channels | 10.0.0 | 10.0.7 | |
|||
| Volo.Abp.* | 10.2.0 | 10.4.0 | |
|||
| Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite | 5.2.0 | 5.4.0 | |
|||
|
|||
|
|||
|
|||
## 数据库迁移 |
|||
|
|||
| Project Name | Database Provider | Scripts | EF Core Version | |
|||
| ------------------------ | ----------------- | ---------------------------------------------- | --------------- | |
|||
| aspire/AuthServer | PostgreSQL | 20260530060327_Add-Identity-User-Inactive | 10.0.2 | |
|||
| services/Single | MySQL | 20260529063257_Add-Identity-User-Inactive | 10.0.3 | |
|||
| services/Single | PostgreSQL | 20260530055941_Add-Identity-User-Inactive | 10.0.2 | |
|||
| services/Single | SqlServer | 20260530060009_Add-Identity-User-Inactive | 10.0.2 | |
|||
| services/AuthServer | MySQL | 20260530061141_Add-Identity-User-Inactive | 10.0.3 | |
|||
|
|||
|
|||
## 数据库连接字符串 |
|||
|
|||
| Module Name | ConnectionString Name | Example(MySQL) | |
|||
| ----------------------------- | ---------------------------- | ------------------------------------------------------------ | |
|||
| Default | Default | Server=127.0.0.1;Database=abp;User Id=root;Password=123456;SslMode=None | |
|||
| abp/Identity | AbpIdentity | Server=127.0.0.1;Database=abp;User Id=root;Password=123456;SslMode=None | |
|||
| abp/IdentityServer | AbpIdentityServer | Server=127.0.0.1;Database=abp;User Id=root;Password=123456;SslMode=None | |
|||
| abp/OpenIddict | AbpOpenIddict | Server=127.0.0.1;Database=abp;User Id=root;Password=123456;SslMode=None | |
|||
| abp/TenantManagement | AbpTenantManagement(AbpSaas) | Server=127.0.0.1;Database=abp;User Id=root;Password=123456;SslMode=None | |
|||
| abp/FeatureManagement | AbpFeatureManagement | Server=127.0.0.1;Database=abp;User Id=root;Password=123456;SslMode=None | |
|||
| abp/SettingManagement | AbpSettingManagement | Server=127.0.0.1;Database=abp;User Id=root;Password=123456;SslMode=None | |
|||
| abp/PermissionManagement | AbpPermissionManagement | Server=127.0.0.1;Database=abp;User Id=root;Password=123456;SslMode=None | |
|||
| labp/AIManagement | AbpAIManagement | Server=127.0.0.1;Database=abp;User Id=root;Password=123456;SslMode=None | |
|||
| labp/BlobManagement | AbpBlobManagement | Server=127.0.0.1;Database=abp;User Id=root;Password=123456;SslMode=None | |
|||
| labp/DataProtectionManagement | AbpDataProtectionManagement | Server=127.0.0.1;Database=abp;User Id=root;Password=123456;SslMode=None | |
|||
| labp/Gdpr | AbpGdpr | Server=127.0.0.1;Database=abp;User Id=root;Password=123456;SslMode=None | |
|||
| labp/Saas | AbpSaas | Server=127.0.0.1;Database=abp;User Id=root;Password=123456;SslMode=None | |
|||
| labp/LocalizationManagement | AbpLocalizationManagement | Server=127.0.0.1;Database=abp;User Id=root;Password=123456;SslMode=None | |
|||
| labp/TextTemplating | AbpTextTemplating | Server=127.0.0.1;Database=abp;User Id=root;Password=123456;SslMode=None | |
|||
| labp/Platform | AppPlatform | Server=127.0.0.1;Database=abp;User Id=root;Password=123456;SslMode=None | |
|||
| labp/MessageService | MessageService | Server=127.0.0.1;Database=abp;User Id=root;Password=123456;SslMode=None | |
|||
| labp/NotificationService | Notifications | Server=127.0.0.1;Database=abp;User Id=root;Password=123456;SslMode=None | |
|||
| labp/TaskManagement | TaskManagement | Server=127.0.0.1;Database=abp;User Id=root;Password=123456;SslMode=None | |
|||
| labp/WebhooksManagement | WebhooksManagement | Server=127.0.0.1;Database=abp;User Id=root;Password=123456;SslMode=None | |
|||
| labp/Elsa | Workflow | Server=127.0.0.1;Database=abp;User Id=root;Password=123456;SslMode=None | |
|||
@ -1,5 +1,6 @@ |
|||
export { useAccountApi } from './useAccountApi'; |
|||
export { useExternalLoginsApi } from './useExternalLoginsApi'; |
|||
export { useLinkUsersApi } from './useLinkUsersApi'; |
|||
export { useMySessionApi } from './useMySessionApi'; |
|||
export { useProfileApi } from './useProfileApi'; |
|||
export { useScanQrCodeApi } from './useScanQrCodeApi'; |
|||
|
|||
@ -0,0 +1,54 @@ |
|||
import type { ListResultDto } from '@abp/core'; |
|||
|
|||
import type { LinkUserDto, UnLinkUserInput } from '../types/link-user'; |
|||
|
|||
import { useRequest } from '@abp/request'; |
|||
|
|||
export function useLinkUsersApi() { |
|||
const { cancel, request } = useRequest(); |
|||
|
|||
/** |
|||
* 查询关联用户列表 |
|||
* @returns { Promise<ListResultDto<LinkUserDto>> } 关联用户列表 |
|||
*/ |
|||
function getListApi(): Promise<ListResultDto<LinkUserDto>> { |
|||
return request<ListResultDto<LinkUserDto>>('api/account/link-users', { |
|||
method: 'GET', |
|||
}); |
|||
} |
|||
/** |
|||
* 生成关联用户Token |
|||
*/ |
|||
function generateLinkTokenApi(): Promise<string> { |
|||
return request(`api/account/link-users/generate-link-token`, { |
|||
method: 'POST', |
|||
}); |
|||
} |
|||
/** |
|||
* 生成关联用户登录Token |
|||
*/ |
|||
function generateLinkLoginTokenApi(): Promise<string> { |
|||
return request(`api/account/link-users/generate-link-login-token`, { |
|||
method: 'POST', |
|||
}); |
|||
} |
|||
|
|||
/** |
|||
* 取消用户关联 |
|||
* @param input 参数 |
|||
*/ |
|||
function unLinkApi(input: UnLinkUserInput): Promise<void> { |
|||
return request(`api/account/link-users/unlink`, { |
|||
method: 'POST', |
|||
data: input, |
|||
}); |
|||
} |
|||
|
|||
return { |
|||
cancel, |
|||
getListApi, |
|||
generateLinkTokenApi, |
|||
generateLinkLoginTokenApi, |
|||
unLinkApi, |
|||
}; |
|||
} |
|||
@ -0,0 +1,198 @@ |
|||
<script setup lang="ts"> |
|||
import type { VxeGridProps } from '@abp/ui'; |
|||
|
|||
import type { LinkUserDto } from '../types'; |
|||
|
|||
import { h, ref } from 'vue'; |
|||
|
|||
import { confirm, useVbenModal } from '@vben/common-ui'; |
|||
import { $t } from '@vben/locales'; |
|||
|
|||
import { sortby } from '@abp/core'; |
|||
import { useMessage, useVbenVxeGrid } from '@abp/ui'; |
|||
import { |
|||
CheckOutlined, |
|||
CloseOutlined, |
|||
DeleteOutlined, |
|||
LoginOutlined, |
|||
PlusOutlined, |
|||
} from '@ant-design/icons-vue'; |
|||
import { Button } from 'ant-design-vue'; |
|||
|
|||
import { useLinkUsersApi } from '../api/useLinkUsersApi'; |
|||
|
|||
const emit = defineEmits<{ |
|||
(event: 'link', token: string): void; |
|||
(event: 'login', userId: string, tenantId?: string): void; |
|||
}>(); |
|||
|
|||
const message = useMessage(); |
|||
const { getListApi, generateLinkTokenApi, unLinkApi } = useLinkUsersApi(); |
|||
|
|||
const linkUsers = ref<LinkUserDto[]>([]); |
|||
|
|||
const [Modal, modalApi] = useVbenModal({ |
|||
async onOpenChange(isOpen) { |
|||
if (isOpen) { |
|||
await onInit(); |
|||
} |
|||
}, |
|||
title: $t('AbpAccount.LinkUser'), |
|||
}); |
|||
const gridOptions: VxeGridProps<LinkUserDto> = { |
|||
columns: [ |
|||
{ |
|||
align: 'left', |
|||
field: 'linkUserName', |
|||
formatter: ({ row }) => { |
|||
if (row.linkTenantName) { |
|||
return `${row.linkTenantName}/${row.linkUserName}`; |
|||
} |
|||
return row.linkUserName; |
|||
}, |
|||
sortable: true, |
|||
title: $t('AbpAccount.DisplayName:LinkUserName'), |
|||
}, |
|||
{ |
|||
align: 'left', |
|||
field: 'directlyLinked', |
|||
slots: { default: 'directlyLinked' }, |
|||
sortable: true, |
|||
title: $t('AbpAccount.DisplayName:DirectlyLinked'), |
|||
width: 150, |
|||
}, |
|||
{ |
|||
field: 'actions', |
|||
fixed: 'right', |
|||
slots: { default: 'actions' }, |
|||
title: $t('AbpUi.Actions'), |
|||
width: 220, |
|||
}, |
|||
], |
|||
exportConfig: {}, |
|||
keepSource: true, |
|||
proxyConfig: { |
|||
ajax: { |
|||
query: async ({ page, sort }) => { |
|||
let items = sortby(linkUsers.value, sort.field); |
|||
if (sort.order === 'desc') { |
|||
items = items.toReversed(); |
|||
} |
|||
const result = { |
|||
totalCount: linkUsers.value.length, |
|||
items: items.slice( |
|||
(page.currentPage - 1) * page.pageSize, |
|||
page.currentPage * page.pageSize, |
|||
), |
|||
}; |
|||
return new Promise((resolve) => { |
|||
resolve(result); |
|||
}); |
|||
}, |
|||
}, |
|||
response: { |
|||
total: 'totalCount', |
|||
list: 'items', |
|||
}, |
|||
}, |
|||
sortConfig: { |
|||
remote: true, |
|||
allowBtn: true, |
|||
}, |
|||
toolbarConfig: { |
|||
custom: true, |
|||
export: true, |
|||
zoom: true, |
|||
}, |
|||
}; |
|||
|
|||
const [Grid, gridApi] = useVbenVxeGrid({ |
|||
gridOptions, |
|||
}); |
|||
|
|||
async function onInit() { |
|||
const { items } = await getListApi(); |
|||
linkUsers.value = items; |
|||
gridApi.query(); |
|||
} |
|||
|
|||
async function onCreatLink() { |
|||
confirm({ |
|||
async beforeClose(scope) { |
|||
if (scope.isConfirm) { |
|||
const token = await generateLinkTokenApi(); |
|||
emit('link', token); |
|||
} |
|||
}, |
|||
centered: true, |
|||
content: $t('AbpAccount.LinkAccountWillBeCreateWarning'), |
|||
title: $t('AbpUi.AreYouSure'), |
|||
}); |
|||
} |
|||
|
|||
function onLoginByLink(record: LinkUserDto) { |
|||
emit('login', record.linkUserId, record.linkTenantId); |
|||
modalApi.close(); |
|||
} |
|||
|
|||
async function onDeleteLink(record: LinkUserDto) { |
|||
confirm({ |
|||
async beforeClose(scope) { |
|||
if (scope.isConfirm) { |
|||
await unLinkApi({ |
|||
userId: record.linkUserId, |
|||
tenantId: record.linkTenantId, |
|||
}); |
|||
message.success($t('AbpUi.DeletedSuccessfully')); |
|||
await onInit(); |
|||
} |
|||
}, |
|||
centered: true, |
|||
content: $t('AbpAccount.LinkAccountWillBeDeleteWarning', [ |
|||
record.linkUserName, |
|||
]), |
|||
title: $t('AbpUi.AreYouSure'), |
|||
}); |
|||
} |
|||
</script> |
|||
|
|||
<template> |
|||
<Modal> |
|||
<Grid> |
|||
<template #toolbar-tools> |
|||
<Button :icon="h(PlusOutlined)" type="primary" @click="onCreatLink"> |
|||
{{ $t('AbpAccount.LinkUser:New') }} |
|||
</Button> |
|||
</template> |
|||
<template #directlyLinked="{ row }"> |
|||
<div class="flex flex-row justify-center"> |
|||
<CheckOutlined v-if="row.directlyLinked" class="text-green-500" /> |
|||
<CloseOutlined v-else class="text-red-500" /> |
|||
</div> |
|||
</template> |
|||
<template #actions="{ row }"> |
|||
<div class="flex flex-row"> |
|||
<Button |
|||
:icon="h(LoginOutlined)" |
|||
block |
|||
type="link" |
|||
@click="onLoginByLink(row)" |
|||
> |
|||
{{ $t('AbpAccount.LinkUser:Login') }} |
|||
</Button> |
|||
<Button |
|||
:icon="h(DeleteOutlined)" |
|||
block |
|||
danger |
|||
type="link" |
|||
@click="onDeleteLink(row)" |
|||
> |
|||
{{ $t('AbpUi.Delete') }} |
|||
</Button> |
|||
</div> |
|||
</template> |
|||
</Grid> |
|||
</Modal> |
|||
</template> |
|||
|
|||
<style scoped></style> |
|||
@ -1,3 +1,4 @@ |
|||
export { default as MyProfile } from './MyProfile.vue'; |
|||
export { default as MySetting } from './MySetting.vue'; |
|||
export { default as QrCodeLogin } from './QrCodeLogin.vue'; |
|||
export { default as UserLinkModal } from './UserLinkModal.vue'; |
|||
|
|||
@ -1,6 +1,8 @@ |
|||
export * from './account'; |
|||
export * from './bind'; |
|||
export * from './external-logins'; |
|||
export * from './link-user'; |
|||
export * from './profile'; |
|||
export * from './token'; |
|||
export * from './user'; |
|||
export type { SigninRedirectArgs } from 'oidc-client-ts'; |
|||
|
|||
@ -0,0 +1,14 @@ |
|||
interface LinkUserDto { |
|||
directlyLinked: boolean; |
|||
linkTenantId?: string; |
|||
linkTenantName?: string; |
|||
linkUserId: string; |
|||
linkUserName: string; |
|||
} |
|||
|
|||
interface UnLinkUserInput { |
|||
tenantId?: string; |
|||
userId: string; |
|||
} |
|||
|
|||
export type { LinkUserDto, UnLinkUserInput }; |
|||
@ -0,0 +1,106 @@ |
|||
<script setup lang="ts"> |
|||
import type { BlobDto } from '../../types/blobs'; |
|||
|
|||
import { defineAsyncComponent, ref } from 'vue'; |
|||
|
|||
import { useVbenModal } from '@vben/common-ui'; |
|||
import { downloadFileFromUrl } from '@vben/utils'; |
|||
|
|||
import { Button, Spin } from 'ant-design-vue'; |
|||
|
|||
import { useBlobsApi } from '../../api/useBlobsApi'; |
|||
|
|||
const VueFilesPreview = defineAsyncComponent({ |
|||
loader: async () => { |
|||
const res = await import('vue-files-preview'); |
|||
import('vue-files-preview/lib/style.css'); |
|||
return res.VueFilesPreview; |
|||
}, |
|||
loadingComponent: Spin, |
|||
delay: 200, |
|||
}); |
|||
|
|||
const fileName = ref<string>(); |
|||
const previewUrl = ref<string>(); |
|||
const errorMsg = ref<string>(); |
|||
|
|||
const { generatePreviewUrlApi } = useBlobsApi(); |
|||
|
|||
const [Modal, modalApi] = useVbenModal({ |
|||
showCancelButton: false, |
|||
showConfirmButton: false, |
|||
fullscreen: true, |
|||
fullscreenButton: false, |
|||
onOpenChange: async (isOpen) => { |
|||
isOpen && onInit(); |
|||
}, |
|||
onClosed() { |
|||
previewUrl.value = undefined; |
|||
fileName.value = undefined; |
|||
errorMsg.value = undefined; |
|||
}, |
|||
}); |
|||
|
|||
async function onInit() { |
|||
try { |
|||
modalApi.lock(); |
|||
const blob = modalApi.getData<BlobDto>(); |
|||
fileName.value = blob.name; |
|||
previewUrl.value = await generatePreviewUrlApi(blob.id); |
|||
} finally { |
|||
modalApi.unlock(); |
|||
} |
|||
} |
|||
|
|||
async function handleDownload() { |
|||
downloadFileFromUrl({ |
|||
fileName: fileName.value!, |
|||
source: previewUrl.value!, |
|||
}); |
|||
} |
|||
|
|||
function onError(error: Error) { |
|||
errorMsg.value = error.message; |
|||
} |
|||
</script> |
|||
|
|||
<template> |
|||
<Modal :title="fileName"> |
|||
<div class="preview-container"> |
|||
<VueFilesPreview |
|||
v-if="previewUrl" |
|||
:url="previewUrl" |
|||
:name="fileName" |
|||
@error="onError" |
|||
/> |
|||
|
|||
<!-- 下载按钮 --> |
|||
<div v-if="errorMsg && previewUrl" class="download-button"> |
|||
<Button type="primary" @click="handleDownload"> |
|||
{{ $t('BlobManagement.Blobs:Download') }} |
|||
</Button> |
|||
</div> |
|||
</div> |
|||
</Modal> |
|||
</template> |
|||
|
|||
<style scoped lang="scss"> |
|||
.preview-container { |
|||
position: relative; |
|||
height: 100%; |
|||
|
|||
.error-message { |
|||
padding: 40px 20px; |
|||
text-align: center; |
|||
} |
|||
|
|||
.download-button { |
|||
position: sticky; |
|||
right: 20px; |
|||
bottom: 20px; |
|||
z-index: 10; |
|||
float: right; |
|||
margin-top: 20px; |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,2 @@ |
|||
/** 启用企业微信认证 */ |
|||
export const WeixinWorkAuthEnable = 'Abp.Account.OAuth.WeCom.Enable'; |
|||
@ -0,0 +1,258 @@ |
|||
using LINGYUN.Abp.SettingManagement; |
|||
using Microsoft.Extensions.Localization; |
|||
using Microsoft.Extensions.Options; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Emailing; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.SettingManagement; |
|||
using Volo.Abp.SettingManagement.Localization; |
|||
using Volo.Abp.Settings; |
|||
using Volo.Abp.Timing; |
|||
using ValueType = Volo.Abp.Settings.ValueType; |
|||
|
|||
namespace LINGYUN.Abp.MicroService.AdminService; |
|||
|
|||
public class SystemGroupSettingDefinitionProvider : SettingDefinitionProvider |
|||
{ |
|||
private const string GroupName = "System"; |
|||
|
|||
protected ITimezoneProvider TimezoneProvider { get; } |
|||
protected AbpLocalizationOptions LocalizationOptions { get; } |
|||
protected IStringLocalizerFactory StringLocalizerFactory { get; } |
|||
|
|||
public SystemGroupSettingDefinitionProvider( |
|||
ITimezoneProvider timezoneProvider, |
|||
IStringLocalizerFactory stringLocalizerFactory, |
|||
IOptions<AbpLocalizationOptions> localizationOptions) |
|||
{ |
|||
TimezoneProvider = timezoneProvider; |
|||
StringLocalizerFactory = stringLocalizerFactory; |
|||
LocalizationOptions = localizationOptions.Value; |
|||
} |
|||
|
|||
public override void Define(ISettingDefinitionContext context) |
|||
{ |
|||
SetSystemSettingGroup(context); |
|||
SetEmailSettingGroup(context); |
|||
} |
|||
|
|||
private void SetSystemSettingGroup(ISettingDefinitionContext context) |
|||
{ |
|||
context.GetOrNull(LocalizationSettingNames.DefaultLanguage) |
|||
?.WithGroup(GroupName, L("Settings:System"), order: 0) |
|||
?.WithParent("Language", L("Settings:System.Language"), order: 0) |
|||
?.WithOrder(0) |
|||
?.WithOptions(LocalizationOptions.Languages.Select(l => new NameValue<string>(l.DisplayName, l.CultureName))) |
|||
?.ReplaceProviders( |
|||
DefaultValueSettingValueProvider.ProviderName, |
|||
ConfigurationSettingValueProvider.ProviderName, |
|||
GlobalSettingValueProvider.ProviderName, |
|||
TenantSettingValueProvider.ProviderName); |
|||
|
|||
var timezoneSetting = context.GetOrNull(TimingSettingNames.TimeZone); |
|||
if (timezoneSetting != null) |
|||
{ |
|||
var stringLocalizer = StringLocalizerFactory.Create<AbpSettingManagementResource>(); |
|||
var timezones = TimeZoneHelper.GetTimezones(TimezoneProvider.GetIanaTimezones()); |
|||
timezones.Insert(0, new NameValue |
|||
{ |
|||
Name = stringLocalizer["DefaultTimeZone"], |
|||
Value = "Unspecified" |
|||
}); |
|||
|
|||
timezoneSetting |
|||
.WithGroup( |
|||
GroupName, |
|||
L("Settings:System"), |
|||
order: 0, |
|||
requiredPermissions: [Volo.Abp.SettingManagement.SettingManagementPermissions.TimeZone]) |
|||
.WithParent("Timing", L("Settings:System.Timing"), order: 0) |
|||
.WithOrder(1) |
|||
.WithOptions(timezones) |
|||
.ReplaceProviders( |
|||
DefaultValueSettingValueProvider.ProviderName, |
|||
ConfigurationSettingValueProvider.ProviderName, |
|||
GlobalSettingValueProvider.ProviderName, |
|||
TenantSettingValueProvider.ProviderName); |
|||
} |
|||
} |
|||
|
|||
private static void SetEmailSettingGroup(ISettingDefinitionContext context) |
|||
{ |
|||
context.GetOrNull(EmailSettingNames.DefaultFromAddress) |
|||
?.WithGroup( |
|||
"Emailing", |
|||
L("Settings:Emailing"), |
|||
order: 1, |
|||
requiredFeatures: [SettingManagementFeatures.Enable, SettingManagementFeatures.AllowChangingEmailSettings], |
|||
requiredPermissions: [Volo.Abp.SettingManagement.SettingManagementPermissions.Emailing]) |
|||
?.WithParent( |
|||
"Default", |
|||
L("Settings:Emailing.Default"), |
|||
order: 0) |
|||
?.WithOrder(0) |
|||
?.WithValueType(ValueType.String) |
|||
?.ReplaceProviders( |
|||
DefaultValueSettingValueProvider.ProviderName, |
|||
ConfigurationSettingValueProvider.ProviderName, |
|||
GlobalSettingValueProvider.ProviderName, |
|||
TenantSettingValueProvider.ProviderName); |
|||
context.GetOrNull(EmailSettingNames.DefaultFromDisplayName) |
|||
?.WithGroup( |
|||
"Emailing", |
|||
L("Settings:Emailing"), |
|||
order: 1, |
|||
requiredFeatures: [SettingManagementFeatures.Enable, SettingManagementFeatures.AllowChangingEmailSettings], |
|||
requiredPermissions: [Volo.Abp.SettingManagement.SettingManagementPermissions.Emailing]) |
|||
?.WithParent( |
|||
"Default", |
|||
L("Settings:Emailing.Default"), |
|||
order: 0) |
|||
?.WithOrder(1) |
|||
?.WithValueType(ValueType.String) |
|||
?.ReplaceProviders( |
|||
DefaultValueSettingValueProvider.ProviderName, |
|||
ConfigurationSettingValueProvider.ProviderName, |
|||
GlobalSettingValueProvider.ProviderName, |
|||
TenantSettingValueProvider.ProviderName); |
|||
|
|||
context.GetOrNull(EmailSettingNames.Smtp.Domain) |
|||
?.WithGroup( |
|||
"Emailing", |
|||
L("Settings:Emailing"), |
|||
order: 1, |
|||
requiredFeatures: [SettingManagementFeatures.Enable, SettingManagementFeatures.AllowChangingEmailSettings], |
|||
requiredPermissions: [Volo.Abp.SettingManagement.SettingManagementPermissions.Emailing]) |
|||
?.WithParent( |
|||
"Smtp", |
|||
L("Settings:Emailing.Smtp"), |
|||
order: 1, |
|||
requiredPermissions: [AbpSettingManagementPermissions.Settings.Manager]) |
|||
?.WithOrder(0) |
|||
?.WithValueType(ValueType.String) |
|||
?.ReplaceProviders( |
|||
DefaultValueSettingValueProvider.ProviderName, |
|||
ConfigurationSettingValueProvider.ProviderName, |
|||
GlobalSettingValueProvider.ProviderName, |
|||
TenantSettingValueProvider.ProviderName); |
|||
context.GetOrNull(EmailSettingNames.Smtp.Host) |
|||
?.WithGroup( |
|||
"Emailing", |
|||
L("Settings:Emailing"), |
|||
order: 1, |
|||
requiredFeatures: [SettingManagementFeatures.Enable, SettingManagementFeatures.AllowChangingEmailSettings], |
|||
requiredPermissions: [Volo.Abp.SettingManagement.SettingManagementPermissions.Emailing]) |
|||
?.WithParent( |
|||
"Smtp", |
|||
L("Settings:Emailing.Smtp"), |
|||
order: 1, |
|||
requiredPermissions: [AbpSettingManagementPermissions.Settings.Manager]) |
|||
?.WithOrder(1) |
|||
?.WithValueType(ValueType.String) |
|||
?.ReplaceProviders( |
|||
DefaultValueSettingValueProvider.ProviderName, |
|||
ConfigurationSettingValueProvider.ProviderName, |
|||
GlobalSettingValueProvider.ProviderName, |
|||
TenantSettingValueProvider.ProviderName); |
|||
context.GetOrNull(EmailSettingNames.Smtp.Port) |
|||
?.WithGroup( |
|||
"Emailing", |
|||
L("Settings:Emailing"), |
|||
order: 1, |
|||
requiredFeatures: [SettingManagementFeatures.Enable, SettingManagementFeatures.AllowChangingEmailSettings], |
|||
requiredPermissions: [Volo.Abp.SettingManagement.SettingManagementPermissions.Emailing]) |
|||
?.WithParent( |
|||
"Smtp", |
|||
L("Settings:Emailing.Smtp"), |
|||
order: 1, |
|||
requiredPermissions: [AbpSettingManagementPermissions.Settings.Manager]) |
|||
?.WithOrder(2) |
|||
?.WithValueType(ValueType.Number) |
|||
?.ReplaceProviders( |
|||
DefaultValueSettingValueProvider.ProviderName, |
|||
ConfigurationSettingValueProvider.ProviderName, |
|||
GlobalSettingValueProvider.ProviderName, |
|||
TenantSettingValueProvider.ProviderName); |
|||
context.GetOrNull(EmailSettingNames.Smtp.UserName) |
|||
?.WithGroup( |
|||
"Emailing", |
|||
L("Settings:Emailing"), |
|||
order: 1, |
|||
requiredFeatures: [SettingManagementFeatures.Enable, SettingManagementFeatures.AllowChangingEmailSettings], |
|||
requiredPermissions: [Volo.Abp.SettingManagement.SettingManagementPermissions.Emailing]) |
|||
?.WithParent( |
|||
"Smtp", |
|||
L("Settings:Emailing.Smtp"), |
|||
order: 1, |
|||
requiredPermissions: [AbpSettingManagementPermissions.Settings.Manager]) |
|||
?.WithOrder(3) |
|||
?.WithValueType(ValueType.String) |
|||
?.ReplaceProviders( |
|||
DefaultValueSettingValueProvider.ProviderName, |
|||
ConfigurationSettingValueProvider.ProviderName, |
|||
GlobalSettingValueProvider.ProviderName, |
|||
TenantSettingValueProvider.ProviderName); |
|||
context.GetOrNull(EmailSettingNames.Smtp.Password) |
|||
?.WithGroup( |
|||
"Emailing", |
|||
L("Settings:Emailing"), |
|||
order: 1, |
|||
requiredFeatures: [SettingManagementFeatures.Enable, SettingManagementFeatures.AllowChangingEmailSettings], |
|||
requiredPermissions: [Volo.Abp.SettingManagement.SettingManagementPermissions.Emailing]) |
|||
?.WithParent( |
|||
"Smtp", |
|||
L("Settings:Emailing.Smtp"), |
|||
order: 1, |
|||
requiredPermissions: [AbpSettingManagementPermissions.Settings.Manager]) |
|||
?.WithOrder(4) |
|||
?.WithValueType(ValueType.String) |
|||
?.ReplaceProviders( |
|||
DefaultValueSettingValueProvider.ProviderName, |
|||
ConfigurationSettingValueProvider.ProviderName, |
|||
GlobalSettingValueProvider.ProviderName, |
|||
TenantSettingValueProvider.ProviderName); |
|||
context.GetOrNull(EmailSettingNames.Smtp.EnableSsl) |
|||
?.WithGroup( |
|||
"Emailing", |
|||
L("Settings:Emailing"), |
|||
order: 1, |
|||
requiredFeatures: [SettingManagementFeatures.Enable, SettingManagementFeatures.AllowChangingEmailSettings], |
|||
requiredPermissions: [Volo.Abp.SettingManagement.SettingManagementPermissions.Emailing]) |
|||
?.WithParent( |
|||
"Smtp", |
|||
L("Settings:Emailing.Smtp"), |
|||
order: 1, |
|||
requiredPermissions: [AbpSettingManagementPermissions.Settings.Manager]) |
|||
?.WithOrder(5) |
|||
?.WithValueType(ValueType.Boolean) |
|||
?.ReplaceProviders( |
|||
DefaultValueSettingValueProvider.ProviderName, |
|||
ConfigurationSettingValueProvider.ProviderName, |
|||
GlobalSettingValueProvider.ProviderName, |
|||
TenantSettingValueProvider.ProviderName); |
|||
context.GetOrNull(EmailSettingNames.Smtp.UseDefaultCredentials) |
|||
?.WithGroup( |
|||
"Emailing", |
|||
L("Settings:Emailing"), |
|||
order: 1, |
|||
requiredFeatures: [SettingManagementFeatures.Enable, SettingManagementFeatures.AllowChangingEmailSettings], |
|||
requiredPermissions: [Volo.Abp.SettingManagement.SettingManagementPermissions.Emailing]) |
|||
?.WithParent( |
|||
"Smtp", |
|||
L("Settings:Emailing.Smtp"), |
|||
order: 1, |
|||
requiredPermissions: [AbpSettingManagementPermissions.Settings.Manager]) |
|||
?.WithOrder(6) |
|||
?.WithValueType(ValueType.Boolean) |
|||
?.ReplaceProviders( |
|||
DefaultValueSettingValueProvider.ProviderName, |
|||
ConfigurationSettingValueProvider.ProviderName, |
|||
GlobalSettingValueProvider.ProviderName, |
|||
TenantSettingValueProvider.ProviderName); |
|||
} |
|||
|
|||
public static LocalizableString L(string name) |
|||
{ |
|||
return LocalizableString.Create<AbpSettingManagementResource>(name); |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
@ -0,0 +1,48 @@ |
|||
using System; |
|||
using Microsoft.EntityFrameworkCore.Migrations; |
|||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; |
|||
|
|||
#nullable disable |
|||
|
|||
namespace LINGYUN.Abp.MicroService.AuthServer.Migrations |
|||
{ |
|||
/// <inheritdoc />
|
|||
public partial class AddIdentityUserInactive : Migration |
|||
{ |
|||
/// <inheritdoc />
|
|||
protected override void Up(MigrationBuilder migrationBuilder) |
|||
{ |
|||
migrationBuilder.CreateTable( |
|||
name: "AbpUserInactives", |
|||
columns: table => new |
|||
{ |
|||
Id = table.Column<long>(type: "bigint", nullable: false) |
|||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), |
|||
UserId = table.Column<Guid>(type: "uuid", nullable: false), |
|||
TenantId = table.Column<Guid>(type: "uuid", nullable: true), |
|||
LastSignInTime = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false), |
|||
LastNotificationTime = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true), |
|||
Status = table.Column<int>(type: "integer", nullable: false), |
|||
DeactivatedTime = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true), |
|||
CreationTime = table.Column<DateTime>(type: "timestamp with time zone", nullable: false), |
|||
CreatorId = table.Column<Guid>(type: "uuid", nullable: true) |
|||
}, |
|||
constraints: table => |
|||
{ |
|||
table.PrimaryKey("PK_AbpUserInactives", x => x.Id); |
|||
}); |
|||
|
|||
migrationBuilder.CreateIndex( |
|||
name: "IX_AbpUserInactives_TenantId_UserId", |
|||
table: "AbpUserInactives", |
|||
columns: new[] { "TenantId", "UserId" }); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
protected override void Down(MigrationBuilder migrationBuilder) |
|||
{ |
|||
migrationBuilder.DropTable( |
|||
name: "AbpUserInactives"); |
|||
} |
|||
} |
|||
} |
|||
@ -1,9 +1,9 @@ |
|||
{ |
|||
"version": "10.2.0", |
|||
"name": "my-app-authserver", |
|||
"version": "10.4.0", |
|||
"name": "aspire-authserver", |
|||
"private": true, |
|||
"dependencies": { |
|||
"@abp/aspnetcore.mvc.ui.theme.leptonxlite": "5.2.0", |
|||
"@abp/qrcode": "10.2.0" |
|||
"@abp/aspnetcore.mvc.ui.theme.leptonxlite": "5.4.0", |
|||
"@abp/qrcode": "10.4.0" |
|||
} |
|||
} |
|||
@ -0,0 +1,120 @@ |
|||
using LINGYUN.Abp.Notifications; |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Logging.Abstractions; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.MultiTenancy; |
|||
using Volo.Abp.Users; |
|||
|
|||
namespace LINGYUN.Abp.MicroService.MessageService.DataSeeds; |
|||
|
|||
public class NotificationDataSeeder : ITransientDependency |
|||
{ |
|||
public ILogger<NotificationDataSeeder> Logger { protected get; set; } |
|||
protected INotificationSender NotificationSender { get; } |
|||
protected INotificationSubscriptionManager NotificationSubscriptionManager { get; } |
|||
public NotificationDataSeeder( |
|||
INotificationSender notificationSender, |
|||
INotificationSubscriptionManager notificationSubscriptionManager) |
|||
{ |
|||
NotificationSender = notificationSender; |
|||
NotificationSubscriptionManager = notificationSubscriptionManager; |
|||
|
|||
Logger = NullLogger<NotificationDataSeeder>.Instance; |
|||
} |
|||
|
|||
public virtual async Task SeedAsync(DataSeedContext context) |
|||
{ |
|||
await SubscribeDefaultNotifierAsync(context); |
|||
await SeedWelcomeNotifierAsync(context); |
|||
} |
|||
|
|||
public async virtual Task SubscribeDefaultNotifierAsync(DataSeedContext context) |
|||
{ |
|||
if (!context.Properties.TryGetValue(MessageServiceDataSeedConsts.AdminUserIdPropertyName, out var userIdString) || |
|||
!Guid.TryParse(userIdString?.ToString(), out var adminUserId)) |
|||
{ |
|||
|
|||
return; |
|||
} |
|||
var adminUserName = context.Properties.GetOrDefault(MessageServiceDataSeedConsts.AdminUserNamePropertyName)?.ToString() |
|||
?? MessageServiceDataSeedConsts.AdminUserNameDefaultValue; |
|||
var userIdentifer = new UserIdentifier(adminUserId, adminUserName); |
|||
// 订阅内置通知
|
|||
await NotificationSubscriptionManager |
|||
.SubscribeAsync( |
|||
context.TenantId, |
|||
userIdentifer, |
|||
DefaultNotifications.SystemNotice); |
|||
await NotificationSubscriptionManager |
|||
.SubscribeAsync( |
|||
context.TenantId, |
|||
userIdentifer, |
|||
DefaultNotifications.OnsideNotice); |
|||
await NotificationSubscriptionManager |
|||
.SubscribeAsync( |
|||
context.TenantId, |
|||
userIdentifer, |
|||
DefaultNotifications.ActivityNotice); |
|||
// 订阅用户欢迎消息
|
|||
await NotificationSubscriptionManager |
|||
.SubscribeAsync( |
|||
context.TenantId, |
|||
userIdentifer, |
|||
UserNotificationNames.WelcomeToApplication); |
|||
} |
|||
|
|||
public async virtual Task SeedWelcomeNotifierAsync(DataSeedContext context) |
|||
{ |
|||
try |
|||
{ |
|||
if (!context.TenantId.HasValue) |
|||
{ |
|||
return; |
|||
} |
|||
if (!context.Properties.TryGetValue(MessageServiceDataSeedConsts.AdminUserIdPropertyName, out var userIdString) || |
|||
!Guid.TryParse(userIdString?.ToString(), out var adminUserId)) |
|||
{ |
|||
return; |
|||
} |
|||
var adminEmailAddress = context.Properties.GetOrDefault(MessageServiceDataSeedConsts.AdminEmailPropertyName)?.ToString() |
|||
?? MessageServiceDataSeedConsts.AdminEmailDefaultValue; |
|||
var adminUserName = context.Properties.GetOrDefault(MessageServiceDataSeedConsts.AdminUserNamePropertyName)?.ToString() |
|||
?? MessageServiceDataSeedConsts.AdminUserNameDefaultValue; |
|||
|
|||
var tenantAdminUserIdentifier = new UserIdentifier(adminUserId, adminEmailAddress); |
|||
|
|||
// 租户管理员订阅事件
|
|||
await NotificationSubscriptionManager |
|||
.SubscribeAsync( |
|||
context.TenantId, |
|||
tenantAdminUserIdentifier, |
|||
TenantNotificationNames.NewTenantRegistered); |
|||
|
|||
Logger.LogInformation("publish new tenant notification.."); |
|||
await NotificationSender.SendNofiterAsync( |
|||
TenantNotificationNames.NewTenantRegistered, |
|||
new NotificationTemplate( |
|||
TenantNotificationNames.NewTenantRegistered, |
|||
formUser: adminEmailAddress, |
|||
data: new Dictionary<string, object> |
|||
{ |
|||
{ "name", adminUserName }, |
|||
{ "email", adminEmailAddress }, |
|||
{ "id", context.TenantId }, |
|||
}), |
|||
tenantAdminUserIdentifier, |
|||
context.TenantId, |
|||
NotificationSeverity.Success); |
|||
|
|||
Logger.LogInformation("tenant administrator subscribes to new tenant events.."); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
Logger.LogWarning(ex, "Failed to send the tenant initialization notification."); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
namespace LINGYUN.Abp.MicroService.MessageService; |
|||
|
|||
public static class MessageServiceDataSeedConsts |
|||
{ |
|||
public const string AdminUserIdPropertyName = "AdminUserId"; |
|||
public const string AdminEmailPropertyName = "AdminEmail"; |
|||
public const string AdminEmailDefaultValue = "admin@abp.io"; |
|||
public const string AdminUserNamePropertyName = "AdminUserName"; |
|||
public const string AdminUserNameDefaultValue = "admin"; |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
using LINGYUN.Abp.MicroService.MessageService.DataSeeds; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace LINGYUN.Abp.MicroService.MessageService; |
|||
|
|||
public class MessageServiceDataSeeder : ITransientDependency |
|||
{ |
|||
protected NotificationDataSeeder NotificationDataSeeder { get; } |
|||
protected ICurrentTenant CurrentTenant { get; } |
|||
public MessageServiceDataSeeder( |
|||
ICurrentTenant currentTenant, |
|||
NotificationDataSeeder notificationDataSeeder) |
|||
{ |
|||
CurrentTenant = currentTenant; |
|||
NotificationDataSeeder = notificationDataSeeder; |
|||
} |
|||
|
|||
public async virtual Task SeedAsync(DataSeedContext context) |
|||
{ |
|||
using (CurrentTenant.Change(context.TenantId)) |
|||
{ |
|||
await SeedNotificationAsync(context); |
|||
} |
|||
} |
|||
|
|||
private async Task SeedNotificationAsync(DataSeedContext context) |
|||
{ |
|||
await NotificationDataSeeder.SeedAsync(context); |
|||
} |
|||
} |
|||
@ -1,70 +0,0 @@ |
|||
using LINGYUN.Abp.Data.DbMigrator; |
|||
using LINGYUN.Abp.MicroService.MessageService.MultiTenancy; |
|||
using LINGYUN.Abp.Notifications; |
|||
using LINGYUN.Abp.Saas.Tenants; |
|||
using Microsoft.Extensions.Logging; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Domain.Entities.Events.Distributed; |
|||
using Volo.Abp.EventBus.Distributed; |
|||
using Volo.Abp.MultiTenancy; |
|||
using Volo.Abp.Uow; |
|||
|
|||
namespace LINGYUN.Abp.MicroService.MessageService.Handlers.Distributed |
|||
{ |
|||
public class TenantSynchronizer : |
|||
IDistributedEventHandler<EntityCreatedEto<TenantEto>>, |
|||
IDistributedEventHandler<EntityUpdatedEto<TenantEto>>, |
|||
IDistributedEventHandler<EntityDeletedEto<TenantEto>>, |
|||
IDistributedEventHandler<TenantConnectionStringUpdatedEto>, |
|||
ITransientDependency |
|||
{ |
|||
protected ILogger<TenantSynchronizer> Logger { get; } |
|||
protected ICurrentTenant CurrentTenant { get; } |
|||
protected IUnitOfWorkManager UnitOfWorkManager { get; } |
|||
protected IDbSchemaMigrator DbSchemaMigrator { get; } |
|||
protected INotificationSender NotificationSender { get; } |
|||
protected ITenantConfigurationCache TenantConfigurationCache { get; } |
|||
protected INotificationSubscriptionManager NotificationSubscriptionManager { get; } |
|||
|
|||
public TenantSynchronizer( |
|||
ICurrentTenant currentTenant, |
|||
IDbSchemaMigrator dbSchemaMigrator, |
|||
IUnitOfWorkManager unitOfWorkManager, |
|||
INotificationSender notificationSender, |
|||
ITenantConfigurationCache tenantConfigurationCache, |
|||
INotificationSubscriptionManager notificationSubscriptionManager, |
|||
ILogger<TenantSynchronizer> logger) |
|||
{ |
|||
Logger = logger; |
|||
|
|||
CurrentTenant = currentTenant; |
|||
DbSchemaMigrator = dbSchemaMigrator; |
|||
UnitOfWorkManager = unitOfWorkManager; |
|||
TenantConfigurationCache = tenantConfigurationCache; |
|||
|
|||
NotificationSender = notificationSender; |
|||
NotificationSubscriptionManager = notificationSubscriptionManager; |
|||
} |
|||
|
|||
public async virtual Task HandleEventAsync(EntityCreatedEto<TenantEto> eventData) |
|||
{ |
|||
await TenantConfigurationCache.RefreshAsync(); |
|||
} |
|||
|
|||
public async virtual Task HandleEventAsync(EntityUpdatedEto<TenantEto> eventData) |
|||
{ |
|||
await TenantConfigurationCache.RefreshAsync(); |
|||
} |
|||
|
|||
public async virtual Task HandleEventAsync(EntityDeletedEto<TenantEto> eventData) |
|||
{ |
|||
await TenantConfigurationCache.RefreshAsync(); |
|||
} |
|||
|
|||
public async virtual Task HandleEventAsync(TenantConnectionStringUpdatedEto eventData) |
|||
{ |
|||
await TenantConfigurationCache.RefreshAsync(); |
|||
} |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue