Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into feature/telemetry
Browse files Browse the repository at this point in the history
  • Loading branch information
guqing committed Dec 6, 2024
2 parents 13777da + d55dd55 commit 3bef54a
Show file tree
Hide file tree
Showing 24 changed files with 5,520 additions and 28 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/cd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,7 @@ jobs:
contents: write
with:
app-id: app-KhIVw
skip-node-setup: true
node-version: "20"
pnpm-version: "9"
ui-path: "ui"
artifacts-path: 'app/build/libs'
4 changes: 3 additions & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,7 @@ jobs:
ci:
uses: halo-sigs/reusable-workflows/.github/workflows/plugin-ci.yaml@v1
with:
skip-node-setup: true
node-version: "20"
pnpm-version: "9"
ui-path: "ui"
artifacts-path: 'app/build/libs'
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ application-local.yml
application-local.yaml
application-local.properties

/admin-frontend/node_modules/
/workplace/
*/workplace/
*/workplace/
/ui/node_modules/
/app/src/main/resources/console/
10 changes: 10 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@ dependencies {
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.register('copyUI', Copy) {
dependsOn ':ui:build'
from project(':ui').layout.buildDirectory.dir('dist')
into layout.buildDirectory.dir('resources/main/console')
}

tasks.named('processResources') {
dependsOn('copyUI')
}

test {
useJUnitPlatform()
}
Expand Down
87 changes: 63 additions & 24 deletions app/src/main/java/run/halo/feed/FeedPluginEndpoint.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package run.halo.feed;

import static org.springframework.web.reactive.function.server.RequestPredicates.accept;
import static org.springframework.web.reactive.function.server.RequestPredicates.path;

import lombok.AllArgsConstructor;
import lombok.Builder;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
Expand All @@ -12,16 +10,17 @@
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.HandlerFunction;
import org.springframework.web.reactive.function.server.RequestPredicate;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.function.server.*;
import reactor.core.publisher.Mono;
import run.halo.app.infra.ExternalUrlSupplier;
import run.halo.app.plugin.extensionpoint.ExtensionGetter;
import run.halo.feed.provider.PostRssProvider;

import java.util.ArrayList;

import static org.springframework.web.reactive.function.server.RequestPredicates.accept;
import static org.springframework.web.reactive.function.server.RequestPredicates.path;

@Component
@AllArgsConstructor
public class FeedPluginEndpoint {
Expand All @@ -33,6 +32,7 @@ public class FeedPluginEndpoint {
private final PostRssProvider postRssProvider;
private final ExtensionGetter extensionGetter;
private final RssCacheManager rssCacheManager;
private final ExternalUrlSupplier externalUrlSupplier;

@Bean
RouterFunction<ServerResponse> rssRouterFunction() {
Expand All @@ -44,6 +44,45 @@ RouterFunction<ServerResponse> rssRouterFunction() {
.build();
}

@Bean
RouterFunction<ServerResponse> rssSourcesListerRouter() {
return RouterFunctions.route()
.GET("/apis/api.feed.halo.run/v1alpha1/rss-sources", this::listRssSources)
.build();
}

private Mono<ServerResponse> listRssSources(ServerRequest request) {
var externalUrl = externalUrlSupplier.getURL(request.exchange().getRequest()).toString();
return extensionGetter.getEnabledExtensions(RssRouteItem.class)
.concatMap(item -> {
var rssSource = RssSource.builder()
.displayName(item.displayName())
.description(item.description())
.example(item.example());
return item.pathPattern()
.map(pattern -> buildPathPattern(pattern, item.namespace()))
.doOnNext(path -> rssSource.pattern(externalUrl + path))
.then(Mono.fromSupplier(rssSource::build));
})
.collectList()
.flatMap(result -> {
var allPosts = RssSource.builder()
.pattern(externalUrl + "/rss.xml")
.displayName("订阅所有文章")
.description("会根据设置的文章数量返回最新的文章")
.example("https://example.com/rss.xml")
.build();
var sources = new ArrayList<>();
sources.add(allPosts);
sources.addAll(result);
return ServerResponse.ok().bodyValue(sources);
});
}

@Builder
record RssSource(String displayName, String description, String pattern, String example) {
}

@Bean
RouterFunction<ServerResponse> additionalRssRouter() {
var pathMatcher = ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, "/feed/**");
Expand Down Expand Up @@ -84,25 +123,25 @@ private Mono<RequestPredicate> buildRequestPredicate(RssRouteItem routeItem) {
.and(ACCEPT_PREDICATE)
);
}
};
}

private String buildPathPattern(String pathPattern, String namespace) {
var sb = new StringBuilder("/feed/");
private String buildPathPattern(String pathPattern, String namespace) {
var sb = new StringBuilder("/feed/");

if (StringUtils.isNotBlank(namespace)) {
sb.append(namespace);
if (!namespace.endsWith("/")) {
sb.append("/");
}
}
if (StringUtils.isNotBlank(namespace)) {
sb.append(namespace);
if (!namespace.endsWith("/")) {
sb.append("/");
}
}

if (pathPattern.startsWith("/")) {
pathPattern = pathPattern.substring(1);
}
sb.append(pathPattern);
if (pathPattern.startsWith("/")) {
pathPattern = pathPattern.substring(1);
}
sb.append(pathPattern);

return sb.toString();
}
};
return sb.toString();
}

private Mono<ServerResponse> buildResponse(String xml) {
Expand Down
14 changes: 14 additions & 0 deletions app/src/main/resources/extensions/roleTemplates.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
apiVersion: v1alpha1
kind: Role
metadata:
name: template-feed-public-apis
labels:
halo.run/role-template: "true"
halo.run/hidden: "true"
rbac.authorization.halo.run/aggregate-to-anonymous: "true"
annotations:
rbac.authorization.halo.run/display-name: "获取订阅源列表"
rules:
- apiGroups: [ "api.feed.halo.run" ]
resources: [ "rss-sources" ]
verbs: [ "list" ]
1 change: 1 addition & 0 deletions app/src/test/java/run/halo/feed/RSS2Test.java
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ void extractRssTagsTest() {
var lastBuildDate = RssXmlBuilder.instantToString(instant);
var rssXml = new RssXmlBuilder()
.withRss2(rss)
.withLastBuildDate(instant)
.withGenerator("Halo")
.withExtractRssTags("""
<user>
Expand Down
1 change: 1 addition & 0 deletions settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ rootProject.name = 'plugin-feed'

include 'api'
include 'app'
include 'ui'
12 changes: 12 additions & 0 deletions ui/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# EditorConfig is awesome: https://EditorConfig.org

# top-most EditorConfig file
root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = false
insert_final_newline = true
23 changes: 23 additions & 0 deletions ui/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* eslint-env node */
require("@rushstack/eslint-patch/modern-module-resolution");

module.exports = {
root: true,
extends: [
"plugin:vue/vue3-recommended",
"eslint:recommended",
"@vue/eslint-config-typescript/recommended",
"@vue/eslint-config-prettier",
"@unocss",
],
env: {
node: true,
"vue/setup-compiler-macros": true,
},
rules: {
"vue/no-v-html": "off",
"vue/no-deprecated-slot-attribute": "off",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-explicit-any": "off",
},
};
30 changes: 30 additions & 0 deletions ui/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
.DS_Store
dist
dist-ssr
coverage
*.local

/cypress/videos/
/cypress/screenshots/

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

*.tsbuildinfo
23 changes: 23 additions & 0 deletions ui/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
plugins {
id "com.github.node-gradle.node" version "7.1.0"
}

tasks.register('clean', Delete) {
delete layout.buildDirectory
}

tasks.register('build', PnpmTask) {
group = 'build'
description = 'Build console.'
dependsOn tasks.named('pnpmInstall')
pnpmCommand = ['run', 'build']
inputs.dir('src')
inputs.files(fileTree('node_modules').exclude('.cache'))
inputs.files(layout.projectDirectory.asFile.listFiles({ pathname ->
pathname.name.endsWithAny(".json", ".yaml", ".env", ".ts", ".json")
} as FileFilter))
outputs.dir(layout.buildDirectory.dir('dist'))
configure {
shouldRunAfter tasks.named('clean')
}
}
29 changes: 29 additions & 0 deletions ui/env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/// <reference types="vite/client" />
/// <reference types="unplugin-icons/types/vue" />
/// <reference types="vue-i18n/dist/vue-i18n.d.ts" />

export {};

declare module "axios" {
export interface AxiosRequestConfig {
mute?: boolean;
}
}

declare module "*.vue" {
import type { ComponentOptions } from "vue";
const Component: ComponentOptions;
export default Component;
}

declare module "*.md" {
import type { ComponentOptions } from "vue";
const Component: ComponentOptions;
export default Component;
}

declare module "vue" {
interface ComponentCustomProperties {
$formkit: any;
}
}
45 changes: 45 additions & 0 deletions ui/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"type": "module",
"scripts": {
"dev": "vite build --watch --mode=development",
"build": "vite build",
"test-unit": "vitest --environment jsdom --run",
"type-check": "vue-tsc -p tsconfig.app.json --noEmit",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
"prettier": "prettier --write src/"
},
"dependencies": {
"@halo-dev/api-client": "^2.20.0",
"@halo-dev/components": "^2.20.0",
"@halo-dev/console-shared": "^2.20.0",
"@tanstack/vue-query": "^4.37.1",
"axios": "^1.7.9",
"vue": "^3.5.13",
"vue-router": "^4.5.0"
},
"devDependencies": {
"@halo-dev/ui-plugin-bundler-kit": "^2.20.0",
"@rushstack/eslint-patch": "^1.10.4",
"@tsconfig/node18": "^2.0.1",
"@types/jsdom": "^20.0.1",
"@types/node": "^18.19.67",
"@unocss/eslint-config": "^0.61.9",
"@vitejs/plugin-vue": "^5.2.1",
"@vue/eslint-config-prettier": "^9.0.0",
"@vue/eslint-config-typescript": "^11.0.3",
"@vue/test-utils": "^2.4.6",
"@vue/tsconfig": "^0.4.0",
"eslint": "^8.57.1",
"eslint-plugin-vue": "^9.32.0",
"jsdom": "^19.0.0",
"prettier": "^3.4.2",
"prettier-plugin-organize-imports": "^4.1.0",
"sass": "^1.82.0",
"typescript": "~5.5.4",
"unocss": "^0.61.9",
"vite": "^5.4.11",
"vitest": "^0.34.6",
"vue-tsc": "^2.1.10"
},
"packageManager": "[email protected]"
}
Loading

0 comments on commit 3bef54a

Please sign in to comment.