edbf78f236
- Halo Plugin 后端(Java/Gradle),含 DarkModePlugin 主类和测试 - Vue 3 + TypeScript 前端 UI,包含主题切换组件和设置页面 - 暗色模式 CSS 变量和覆盖样式(布局/编辑器/表单/滚动条等) - 设计文档和调查文档 - Halo 插件/主题开发 Agent Skills Co-Authored-By: Claude <noreply@anthropic.com>
340 lines
13 KiB
Markdown
340 lines
13 KiB
Markdown
# Shared Beans (Dependency Injection)
|
|
|
|
Halo exposes several core beans that any plugin can inject via constructor injection.
|
|
|
|
> Source references (Halo main branch):
|
|
>
|
|
> - [ReactiveExtensionClient](https://github.com/halo-dev/halo/blob/main/api/src/main/java/run/halo/app/extension/ReactiveExtensionClient.java)
|
|
> - [ExtensionClient](https://github.com/halo-dev/halo/blob/main/api/src/main/java/run/halo/app/extension/ExtensionClient.java)
|
|
> - [SchemeManager](https://github.com/halo-dev/halo/blob/main/api/src/main/java/run/halo/app/extension/SchemeManager.java)
|
|
> - [ExtensionGetter](https://github.com/halo-dev/halo/blob/main/api/src/main/java/run/halo/app/plugin/extensionpoint/ExtensionGetter.java)
|
|
> - [UserService](https://github.com/halo-dev/halo/blob/main/api/src/main/java/run/halo/app/core/user/service/UserService.java)
|
|
> - [RoleService](https://github.com/halo-dev/halo/blob/main/api/src/main/java/run/halo/app/core/user/service/RoleService.java)
|
|
> - [AttachmentService](https://github.com/halo-dev/halo/blob/main/api/src/main/java/run/halo/app/core/extension/service/AttachmentService.java)
|
|
> - [PostContentService](https://github.com/halo-dev/halo/blob/main/api/src/main/java/run/halo/app/content/PostContentService.java)
|
|
> - [ExternalLinkProcessor](https://github.com/halo-dev/halo/blob/main/api/src/main/java/run/halo/app/infra/ExternalLinkProcessor.java)
|
|
> - [ExternalUrlSupplier](https://github.com/halo-dev/halo/blob/main/api/src/main/java/run/halo/app/infra/ExternalUrlSupplier.java)
|
|
> - [NotificationReasonEmitter](https://github.com/halo-dev/halo/blob/main/api/src/main/java/run/halo/app/notification/NotificationReasonEmitter.java)
|
|
> - [NotificationCenter](https://github.com/halo-dev/halo/blob/main/api/src/main/java/run/halo/app/notification/NotificationCenter.java)
|
|
> - [CryptoService](https://github.com/halo-dev/halo/blob/main/api/src/main/java/run/halo/app/security/authentication/CryptoService.java)
|
|
> - [RateLimiterRegistry](https://github.com/resilience4j/resilience4j/blob/master/resilience4j-ratelimiter/src/main/java/io/github/resilience4j/ratelimiter/RateLimiterRegistry.java)
|
|
> - [ReactiveSettingFetcher](https://github.com/halo-dev/halo/blob/main/api/src/main/java/run/halo/app/plugin/ReactiveSettingFetcher.java)
|
|
> - [SettingFetcher](https://github.com/halo-dev/halo/blob/main/api/src/main/java/run/halo/app/plugin/SettingFetcher.java)
|
|
> - [PluginConfigUpdatedEvent](https://github.com/halo-dev/halo/blob/main/api/src/main/java/run/halo/app/plugin/PluginConfigUpdatedEvent.java)
|
|
> - [JsonUtils](https://github.com/halo-dev/halo/blob/main/api/src/main/java/run/halo/app/infra/utils/JsonUtils.java)
|
|
> - [ExtensionUtil](https://github.com/halo-dev/halo/blob/main/api/src/main/java/run/halo/app/extension/ExtensionUtil.java)
|
|
> - [MetadataUtil](https://github.com/halo-dev/halo/blob/main/api/src/main/java/run/halo/app/extension/MetadataUtil.java)
|
|
> - [PageRequestImpl](https://github.com/halo-dev/halo/blob/main/api/src/main/java/run/halo/app/extension/PageRequestImpl.java)
|
|
> - [SortResolver](https://github.com/halo-dev/halo/blob/main/api/src/main/java/run/halo/app/core/extension/endpoint/SortResolver.java)
|
|
> - [AnonymousUserConst](https://github.com/halo-dev/halo/blob/main/api/src/main/java/run/halo/app/infra/AnonymousUserConst.java)
|
|
|
|
## Docs Routing
|
|
|
|
This file lists frequently used beans, not every injectable type in Halo. For
|
|
method signatures and newer APIs, verify against the official docs or source
|
|
before coding.
|
|
|
|
| Need | Official docs |
|
|
| ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
| ExtensionClient / ReactiveExtensionClient | https://raw.githubusercontent.com/halo-dev/docs/refs/heads/main/docs/developer-guide/plugin/api-reference/server/extension-client.md |
|
|
| ExtensionGetter and extension points | https://raw.githubusercontent.com/halo-dev/docs/refs/heads/main/docs/developer-guide/plugin/api-reference/server/extension-getter.md |
|
|
| SettingFetcher / ReactiveSettingFetcher | https://raw.githubusercontent.com/halo-dev/docs/refs/heads/main/docs/developer-guide/plugin/api-reference/server/setting-fetcher.md |
|
|
| Notifications | https://raw.githubusercontent.com/halo-dev/docs/refs/heads/main/docs/developer-guide/plugin/api-reference/server/notification.md |
|
|
| Reverse proxy helpers | https://raw.githubusercontent.com/halo-dev/docs/refs/heads/main/docs/developer-guide/plugin/api-reference/server/reverseproxy.md |
|
|
| WebSocket helpers | https://raw.githubusercontent.com/halo-dev/docs/refs/heads/main/docs/developer-guide/plugin/api-reference/server/websocket.md |
|
|
| Login handler enhancement | https://raw.githubusercontent.com/halo-dev/docs/refs/heads/main/docs/developer-guide/plugin/api-reference/server/login-handler-enhancer.md |
|
|
| Template and finder helpers for themes | https://raw.githubusercontent.com/halo-dev/docs/refs/heads/main/docs/developer-guide/plugin/api-reference/server/template-for-theme.md |
|
|
|
|
When working from a local docs checkout, use the same paths under
|
|
`docs/developer-guide/...`; versioned docs live under
|
|
`versioned_docs/version-2.25/...`.
|
|
|
|
## Quick Index
|
|
|
|
- Custom model CRUD: [ReactiveExtensionClient](#reactiveextensionclient)
|
|
- Blocking model CRUD: [ExtensionClient](#extensionclient)
|
|
- Extension point lookup: [ExtensionGetter](#extensiongetter)
|
|
- Plugin settings: [SettingFetcher / ReactiveSettingFetcher](#settingfetcher--reactivesettingfetcher)
|
|
- Notifications: [Notifications](#notifications)
|
|
- JSON helpers: [JsonUtils](#jsonutils)
|
|
|
|
## ReactiveExtensionClient
|
|
|
|
Reactive CRUD for custom extensions.
|
|
|
|
```java
|
|
private final ReactiveExtensionClient client;
|
|
|
|
// List with options
|
|
client.listBy(Person.class, query.toListOptions(), query.toPageRequest());
|
|
|
|
// Get by name
|
|
client.fetch(Person.class, "person-name");
|
|
|
|
// Create
|
|
client.create(person);
|
|
|
|
// Update
|
|
client.update(person);
|
|
|
|
// Delete
|
|
client.delete(person);
|
|
```
|
|
|
|
## ExtensionClient
|
|
|
|
Blocking version of `ReactiveExtensionClient`. Use only in non-NIO threads (e.g., background tasks).
|
|
|
|
## SchemeManager
|
|
|
|
Register/unregister custom extension types.
|
|
|
|
```java
|
|
schemeManager.register(Person.class);
|
|
schemeManager.register(Person.class, indexSpecs -> { /* ... */ });
|
|
schemeManager.unregister(Scheme.buildFromType(Person.class));
|
|
```
|
|
|
|
## ExtensionGetter
|
|
|
|
Retrieve implementations of an extension point.
|
|
|
|
```java
|
|
private final ExtensionGetter extensionGetter;
|
|
|
|
// Get all implementations
|
|
extensionGetter.getExtensions(AttachmentHandler.class);
|
|
```
|
|
|
|
## UserService
|
|
|
|
User operations: get info, update password, create users.
|
|
|
|
## ReactiveUserDetailsService
|
|
|
|
```java
|
|
Mono<UserDetails> findByUsername(String username);
|
|
```
|
|
|
|
## RoleService
|
|
|
|
Role operations: query roles, bindings, dependencies.
|
|
|
|
## AttachmentService
|
|
|
|
Upload, delete, get access URLs for attachments.
|
|
|
|
## PostContentService
|
|
|
|
Get post content with version management.
|
|
|
|
```java
|
|
postContentService.getHeadContent(postName); // latest draft
|
|
postContentService.getReleaseContent(postName); // published version
|
|
postContentService.getSpecifiedContent(snapshotName);
|
|
postContentService.listSnapshots(postName);
|
|
```
|
|
|
|
## ExternalLinkProcessor
|
|
|
|
Convert relative URLs to absolute using configured `externalUrl`.
|
|
|
|
```java
|
|
externalLinkProcessor.processLink("/post/1");
|
|
// -> "https://example.com/post/1"
|
|
```
|
|
|
|
## ExternalUrlSupplier
|
|
|
|
Get the configured external URL.
|
|
|
|
## NotificationReasonEmitter / NotificationCenter
|
|
|
|
Send notifications and manage subscriptions.
|
|
|
|
## ServerSecurityContextRepository
|
|
|
|
Access authentication context. Required if your filter runs before Spring Security's `ReactorContextWebFilter`.
|
|
|
|
## CryptoService
|
|
|
|
Decrypt login passwords or reuse the public key.
|
|
|
|
```java
|
|
cryptoService.readPublicKey();
|
|
cryptoService.decrypt(encryptedPassword);
|
|
```
|
|
|
|
## RateLimiterRegistry
|
|
|
|
Create rate limiters (remember to clean up in `stop()`).
|
|
|
|
```java
|
|
var rateLimiter = rateLimiterRegistry.rateLimiter(key,
|
|
new RateLimiterConfig.Builder()
|
|
.limitForPeriod(1)
|
|
.limitRefreshPeriod(Duration.ofSeconds(60))
|
|
.build());
|
|
```
|
|
|
|
## SystemInfoGetter (Halo 2.20.11+)
|
|
|
|
```java
|
|
Mono<SystemInfo> info = systemInfoGetter.get();
|
|
// Contains: title, subtitle, logo, favicon, url, version, seo, locale, timeZone, activatedThemeName
|
|
```
|
|
|
|
## ReactiveSettingFetcher / SettingFetcher
|
|
|
|
Fetch plugin configuration defined in `plugin.yaml` (`settingName` / `configMapName`).
|
|
The fetcher caches values internally and auto-refreshes when configuration changes.
|
|
|
|
**Reactive (WebFlux):**
|
|
|
|
```java
|
|
private final ReactiveSettingFetcher settingFetcher;
|
|
|
|
// Fetch a typed config object by group name
|
|
settingFetcher.fetch("seo", SeoSetting.class)
|
|
.doOnNext(seo -> { ... });
|
|
|
|
// Get raw JsonNode by group
|
|
settingFetcher.getSettingValue("seo");
|
|
|
|
// Get all groups as a map
|
|
settingFetcher.getSettingValues();
|
|
```
|
|
|
|
**Blocking (background tasks / non-reactive code):**
|
|
|
|
```java
|
|
private final SettingFetcher settingFetcher;
|
|
|
|
Optional<SeoSetting> seo = settingFetcher.fetch("seo", SeoSetting.class);
|
|
JsonNode raw = settingFetcher.getSettingValue("seo");
|
|
Map<String, JsonNode> all = settingFetcher.getSettingValues();
|
|
```
|
|
|
|
**Listen for config changes:**
|
|
|
|
```java
|
|
@EventListener
|
|
public void onConfigUpdated(PluginConfigUpdatedEvent event) {
|
|
if (event.getNewConfig().containsKey("seo")) {
|
|
// re-apply configuration
|
|
}
|
|
}
|
|
```
|
|
|
|
> **Prerequisite**: In `plugin.yaml`, declare `settingName: my-settings` and `configMapName: my-configmap`, and create a corresponding `settings.yaml` extension resource defining the form schema and groups.
|
|
|
|
## JsonUtils
|
|
|
|
JSON serialization/deserialization utilities.
|
|
|
|
> Note: `@Deprecated(forRemoval = true, since = "2.23.0")` — prefer `tools.jackson.databind.json.JsonMapper` in newer Halo versions.
|
|
|
|
```java
|
|
// Object to JSON string
|
|
String json = JsonUtils.objectToJson(myObject);
|
|
|
|
// JSON string to object
|
|
MyObject obj = JsonUtils.jsonToObject(json, MyObject.class);
|
|
|
|
// JSON string with generics
|
|
Set<String> tags = JsonUtils.jsonToObject(json, new TypeReference<>() {});
|
|
|
|
// Map to object
|
|
MyObject obj = JsonUtils.mapToObject(map, MyObject.class);
|
|
|
|
// Deep copy
|
|
MyObject copy = JsonUtils.deepCopy(original);
|
|
|
|
// Access the underlying ObjectMapper
|
|
ObjectMapper mapper = JsonUtils.mapper();
|
|
mapper.convertValue(data, new TypeReference<>() {});
|
|
```
|
|
|
|
## ExtensionUtil / MetadataUtil
|
|
|
|
Utility methods for Extension lifecycle and metadata.
|
|
|
|
```java
|
|
// Check deletion state
|
|
boolean deleted = ExtensionUtil.isDeleted(extension);
|
|
|
|
// Query for non-deleted resources (for ListOptions)
|
|
ListOptions.builder().fieldQuery(ExtensionUtil.notDeleting()).build();
|
|
|
|
// Finalizer management
|
|
ExtensionUtil.addFinalizers(metadata, Set.of("my-plugin/finalizer"));
|
|
ExtensionUtil.removeFinalizers(metadata, Set.of("my-plugin/finalizer"));
|
|
|
|
// Default sort: creationTimestamp desc, name asc
|
|
Sort sort = ExtensionUtil.defaultSort();
|
|
|
|
// Safe metadata access (auto-initializes null collections)
|
|
Map<String, String> labels = MetadataUtil.nullSafeLabels(extension);
|
|
Map<String, String> annotations = MetadataUtil.nullSafeAnnotations(extension);
|
|
```
|
|
|
|
## PageRequestImpl / PageRequest
|
|
|
|
Pagination for list queries. Page numbers are **1-based**.
|
|
|
|
```java
|
|
// Basic pagination
|
|
PageRequestImpl.of(page, size); // page >= 1, size <= 1000
|
|
PageRequestImpl.of(page, size, Sort.by("name"));
|
|
PageRequestImpl.ofSize(size); // page 1, given size
|
|
|
|
// With sorting
|
|
PageRequestImpl.of(1, 20, Sort.by(
|
|
Sort.Order.desc("metadata.creationTimestamp"),
|
|
Sort.Order.asc("metadata.name")
|
|
));
|
|
```
|
|
|
|
## SortResolver / SortableRequest
|
|
|
|
Resolve sorting from HTTP query parameters.
|
|
|
|
```java
|
|
// In a CustomEndpoint handler
|
|
Sort sort = SortResolver.defaultInstance.resolve(exchange);
|
|
|
|
// Or extend SortableRequest for standardized list queries
|
|
public class MyQuery extends SortableRequest {
|
|
public MyQuery(ServerWebExchange exchange) { super(exchange); }
|
|
|
|
public ListOptions toListOptions() {
|
|
return labelAndFieldSelectorToListOptions(getLabelSelector(), getFieldSelector());
|
|
}
|
|
}
|
|
|
|
// Usage
|
|
MyQuery query = new MyQuery(request.exchange());
|
|
client.listBy(MyExtension.class, query.toListOptions(), query.toPageRequest());
|
|
```
|
|
|
|
Query parameter format: `?sort=field,asc` or `?sort=field,desc`. Multiple sorts: `?sort=field1,asc&sort=field2,desc`.
|
|
|
|
## AnonymousUserConst
|
|
|
|
Check for anonymous users:
|
|
|
|
```java
|
|
if (AnonymousUserConst.isAnonymousUser(authentication.getName())) {
|
|
// Handle anonymous user
|
|
}
|
|
// Constants: AnonymousUserConst.PRINCIPAL ("anonymousUser"), AnonymousUserConst.Role ("anonymous")
|
|
```
|
|
|
|
## BackupRootGetter / PluginsRootGetter
|
|
|
|
`Supplier<Path>` for backup directory and plugin directory.
|
|
|
|
## LoginHandlerEnhancer
|
|
|
|
Hook into login success/failure for custom logic.
|