├── gradle.properties ├── console ├── src │ ├── api │ │ ├── generated │ │ │ ├── .openapi-generator │ │ │ │ ├── VERSION │ │ │ │ └── FILES │ │ │ ├── .gitignore │ │ │ ├── .npmignore │ │ │ ├── index.ts │ │ │ ├── models │ │ │ │ ├── index.ts │ │ │ │ ├── link-group-spec.ts │ │ │ │ ├── link-detail-dto.ts │ │ │ │ ├── remove-operation.ts │ │ │ │ ├── add-operation.ts │ │ │ │ ├── test-operation.ts │ │ │ │ ├── link.ts │ │ │ │ ├── copy-operation.ts │ │ │ │ ├── move-operation.ts │ │ │ │ ├── replace-operation.ts │ │ │ │ ├── link-spec.ts │ │ │ │ ├── link-group.ts │ │ │ │ ├── json-patch-inner.ts │ │ │ │ ├── metadata.ts │ │ │ │ ├── link-list.ts │ │ │ │ └── link-group-list.ts │ │ │ ├── api.ts │ │ │ ├── .openapi-generator-ignore │ │ │ ├── git_push.sh │ │ │ ├── base.ts │ │ │ ├── configuration.ts │ │ │ ├── common.ts │ │ │ └── api │ │ │ │ └── api-plugin-halo-run-v1alpha1-link-api.ts │ │ └── index.ts │ ├── utils │ │ └── date.ts │ ├── index.ts │ ├── composables │ │ └── use-link.ts │ ├── components │ │ ├── GroupEditingModal.vue │ │ ├── GroupList.vue │ │ └── LinkEditingModal.vue │ ├── assets │ │ └── logo.svg │ └── views │ │ └── LinkList.vue ├── pnpm-workspace.yaml ├── env.d.ts ├── prettier.config.cjs ├── uno.config.ts ├── .editorconfig ├── tsconfig.json ├── .gitignore ├── .eslintrc.cjs ├── rsbuild.config.mjs └── package.json ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── settings.gradle ├── OWNERS ├── .github └── workflows │ ├── ci.yaml │ └── cd.yaml ├── src └── main │ ├── resources │ ├── extensions │ │ ├── settings.yaml │ │ └── roleTemplate.yaml │ ├── plugin.yaml │ └── logo.svg │ └── java │ └── run │ └── halo │ └── links │ ├── finders │ ├── LinkFinder.java │ └── impl │ │ └── LinkFinderImpl.java │ ├── LinkDetailDTO.java │ ├── vo │ ├── LinkVo.java │ └── LinkGroupVo.java │ ├── Link.java │ ├── LinkGroup.java │ ├── LinkCommentSubject.java │ ├── LinkRequest.java │ ├── LinkPlugin.java │ └── LinkRouter.java ├── .gitignore ├── gradlew.bat ├── README.md ├── gradlew ├── .editorconfig └── api-docs └── openapi └── v3_0 └── linksV1alpha1Api.json /gradle.properties: -------------------------------------------------------------------------------- 1 | version=1.2.0-SNAPSHOT 2 | -------------------------------------------------------------------------------- /console/src/api/generated/.openapi-generator/VERSION: -------------------------------------------------------------------------------- 1 | 7.7.0 2 | -------------------------------------------------------------------------------- /console/src/api/generated/.gitignore: -------------------------------------------------------------------------------- 1 | wwwroot/*.js 2 | node_modules 3 | typings 4 | dist 5 | -------------------------------------------------------------------------------- /console/pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | onlyBuiltDependencies: 2 | - core-js 3 | - esbuild 4 | - vue-demi 5 | -------------------------------------------------------------------------------- /console/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halo-sigs/plugin-links/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /console/src/api/generated/.npmignore: -------------------------------------------------------------------------------- 1 | # empty npmignore to ensure all required files (e.g., in the dist folder) are published by npm -------------------------------------------------------------------------------- /console/prettier.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | printWidth: 120, 3 | tabWidth: 2, 4 | useTabs: false, 5 | endOfLine: "lf", 6 | }; 7 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | } 5 | } 6 | rootProject.name = 'plugin-links' 7 | 8 | -------------------------------------------------------------------------------- /OWNERS: -------------------------------------------------------------------------------- 1 | reviewers: 2 | - ruibaby 3 | - guqing 4 | - JohnNiang 5 | - wangzhen-fit2cloud 6 | 7 | approvers: 8 | - ruibaby 9 | - guqing 10 | - JohnNiang 11 | -------------------------------------------------------------------------------- /console/uno.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, presetWind3, transformerCompileClass } from "unocss"; 2 | 3 | export default defineConfig({ 4 | presets: [presetWind3()], 5 | transformers: [transformerCompileClass()], 6 | }); 7 | -------------------------------------------------------------------------------- /console/.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*] 7 | indent_style = space 8 | indent_size = 2 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = false 12 | insert_final_newline = true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | ci: 13 | uses: halo-sigs/reusable-workflows/.github/workflows/plugin-ci.yaml@v3 14 | with: 15 | node-version: 20 16 | pnpm-version: 10 17 | ui-path: "console" 18 | -------------------------------------------------------------------------------- /src/main/resources/extensions/settings.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1alpha1 2 | kind: Setting 3 | metadata: 4 | name: plugin-links-settings 5 | spec: 6 | forms: 7 | - group: base 8 | label: 基本设置 9 | formSchema: 10 | - $formkit: text 11 | label: 页面标题 12 | name: title 13 | validation: required 14 | value: '链接' -------------------------------------------------------------------------------- /console/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@vue/tsconfig/tsconfig.web.json", 3 | "include": ["./env.d.ts", "./src/**/*", "./src/**/*.vue"], 4 | "exclude": ["./src/**/__tests__/*"], 5 | "compilerOptions": { 6 | "composite": true, 7 | "baseUrl": ".", 8 | "paths": { 9 | "@/*": ["./src/*"] 10 | }, 11 | "types": ["unplugin-icons/types/vue"] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /console/src/utils/date.ts: -------------------------------------------------------------------------------- 1 | import dayjs from "dayjs"; 2 | import "dayjs/locale/zh-cn"; 3 | import timezone from "dayjs/plugin/timezone"; 4 | 5 | dayjs.extend(timezone); 6 | 7 | dayjs.locale("zh-cn"); 8 | 9 | export function formatDatetime(date: string | Date | undefined | null): string { 10 | if (!date) { 11 | return ""; 12 | } 13 | return dayjs(date).format("YYYY-MM-DD HH:mm"); 14 | } 15 | -------------------------------------------------------------------------------- /.github/workflows/cd.yaml: -------------------------------------------------------------------------------- 1 | name: CD 2 | 3 | on: 4 | release: 5 | types: 6 | - published 7 | 8 | jobs: 9 | cd: 10 | uses: halo-sigs/reusable-workflows/.github/workflows/plugin-cd.yaml@v3 11 | secrets: 12 | halo-pat: ${{ secrets.HALO_PAT }} 13 | permissions: 14 | contents: write 15 | with: 16 | node-version: 20 17 | pnpm-version: 10 18 | app-id: app-hfbQg 19 | ui-path: "console" 20 | -------------------------------------------------------------------------------- /console/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | .DS_Store 12 | dist 13 | dist-ssr 14 | coverage 15 | *.local 16 | 17 | /cypress/videos/ 18 | /cypress/screenshots/ 19 | 20 | # Editor directories and files 21 | .vscode/* 22 | !.vscode/extensions.json 23 | .idea 24 | *.suo 25 | *.ntvs* 26 | *.njsproj 27 | *.sln 28 | *.sw? 29 | -------------------------------------------------------------------------------- /src/main/java/run/halo/links/finders/LinkFinder.java: -------------------------------------------------------------------------------- 1 | package run.halo.links.finders; 2 | 3 | import reactor.core.publisher.Flux; 4 | import run.halo.links.vo.LinkGroupVo; 5 | import run.halo.links.vo.LinkVo; 6 | 7 | /** 8 | * A finder for {@link run.halo.links.Link}. 9 | * 10 | * @author guqing 11 | * @author ryanwang 12 | */ 13 | public interface LinkFinder { 14 | 15 | Flux listBy(String group); 16 | 17 | Flux groupBy(); 18 | } 19 | -------------------------------------------------------------------------------- /console/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | require("@rushstack/eslint-patch/modern-module-resolution"); 3 | 4 | module.exports = { 5 | root: true, 6 | extends: [ 7 | "plugin:vue/vue3-recommended", 8 | "eslint:recommended", 9 | "@vue/eslint-config-typescript/recommended", 10 | "@vue/eslint-config-prettier", 11 | "@unocss", 12 | ], 13 | env: { 14 | "vue/setup-compiler-macros": true, 15 | }, 16 | rules: { 17 | "@unocss/enforce-class-compile": 1, 18 | }, 19 | }; 20 | -------------------------------------------------------------------------------- /console/src/api/index.ts: -------------------------------------------------------------------------------- 1 | import { axiosInstance } from "@halo-dev/api-client"; 2 | import { ApiPluginHaloRunV1alpha1LinkApi, LinkGroupV1alpha1Api, LinkV1alpha1Api } from "./generated"; 3 | 4 | const linksCoreApiClient = { 5 | link: new LinkV1alpha1Api(undefined, "", axiosInstance), 6 | group: new LinkGroupV1alpha1Api(undefined, "", axiosInstance), 7 | }; 8 | 9 | const linksConsoleApiClient = { 10 | link: new ApiPluginHaloRunV1alpha1LinkApi(undefined, "", axiosInstance), 11 | }; 12 | 13 | export { linksConsoleApiClient, linksCoreApiClient }; 14 | -------------------------------------------------------------------------------- /src/main/java/run/halo/links/LinkDetailDTO.java: -------------------------------------------------------------------------------- 1 | package run.halo.links; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import lombok.Data; 5 | 6 | /** 7 | * @author LIlGG 8 | */ 9 | @Data 10 | public class LinkDetailDTO { 11 | 12 | @JsonProperty(value = "title", required = true) 13 | private String title; 14 | 15 | @JsonProperty("description") 16 | private String description; 17 | 18 | @JsonProperty("icon") 19 | private String icon; 20 | 21 | @JsonProperty("image") 22 | private String image; 23 | } 24 | -------------------------------------------------------------------------------- /console/src/api/generated/index.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Halo 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 2.17.2 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | 16 | export * from "./api"; 17 | export * from "./configuration"; 18 | export * from "./models"; 19 | -------------------------------------------------------------------------------- /console/src/api/generated/models/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./add-operation"; 2 | export * from "./copy-operation"; 3 | export * from "./json-patch-inner"; 4 | export * from "./link"; 5 | export * from "./link-detail-dto"; 6 | export * from "./link-group"; 7 | export * from "./link-group-list"; 8 | export * from "./link-group-spec"; 9 | export * from "./link-list"; 10 | export * from "./link-spec"; 11 | export * from "./metadata"; 12 | export * from "./move-operation"; 13 | export * from "./remove-operation"; 14 | export * from "./replace-operation"; 15 | export * from "./test-operation"; 16 | -------------------------------------------------------------------------------- /console/src/api/generated/api.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Halo 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 2.17.2 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | 16 | 17 | export * from './api/api-plugin-halo-run-v1alpha1-link-api'; 18 | export * from './api/link-group-v1alpha1-api'; 19 | export * from './api/link-v1alpha1-api'; 20 | 21 | -------------------------------------------------------------------------------- /src/main/java/run/halo/links/vo/LinkVo.java: -------------------------------------------------------------------------------- 1 | package run.halo.links.vo; 2 | 3 | import lombok.Builder; 4 | import lombok.Value; 5 | import run.halo.app.extension.MetadataOperator; 6 | import run.halo.app.theme.finders.vo.ExtensionVoOperator; 7 | import run.halo.links.Link; 8 | 9 | /** 10 | * @author guqing 11 | * @since 2.0.0 12 | */ 13 | @Value 14 | @Builder 15 | public class LinkVo implements ExtensionVoOperator { 16 | 17 | MetadataOperator metadata; 18 | 19 | Link.LinkSpec spec; 20 | 21 | public static LinkVo from(Link link) { 22 | return LinkVo.builder() 23 | .metadata(link.getMetadata()) 24 | .spec(link.getSpec()) 25 | .build(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /console/src/api/generated/.openapi-generator/FILES: -------------------------------------------------------------------------------- 1 | .gitignore 2 | .npmignore 3 | .openapi-generator-ignore 4 | api.ts 5 | api/api-plugin-halo-run-v1alpha1-link-api.ts 6 | api/link-group-v1alpha1-api.ts 7 | api/link-v1alpha1-api.ts 8 | base.ts 9 | common.ts 10 | configuration.ts 11 | git_push.sh 12 | index.ts 13 | models/add-operation.ts 14 | models/copy-operation.ts 15 | models/index.ts 16 | models/json-patch-inner.ts 17 | models/link-detail-dto.ts 18 | models/link-group-list.ts 19 | models/link-group-spec.ts 20 | models/link-group.ts 21 | models/link-list.ts 22 | models/link-spec.ts 23 | models/link.ts 24 | models/metadata.ts 25 | models/move-operation.ts 26 | models/remove-operation.ts 27 | models/replace-operation.ts 28 | models/test-operation.ts 29 | -------------------------------------------------------------------------------- /src/main/resources/plugin.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: plugin.halo.run/v1alpha1 2 | kind: Plugin 3 | metadata: 4 | name: PluginLinks 5 | annotations: 6 | "store.halo.run/app-id": "app-hfbQg" 7 | spec: 8 | requires: ">=2.21.0" 9 | author: 10 | name: Halo 11 | website: https://github.com/halo-dev 12 | logo: logo.svg 13 | configMapName: plugin-links-configmap 14 | settingName: plugin-links-settings 15 | repo: https://github.com/halo-sigs/plugin-links 16 | homepage: https://www.halo.run/store/apps/app-hfbQg 17 | issues: https://github.com/halo-sigs/plugin-links/issues 18 | displayName: "链接管理" 19 | description: "链接管理模块,可用于管理网站友情链接。" 20 | license: 21 | - name: "GPL-3.0" 22 | url: "https://github.com/halo-sigs/plugin-links/blob/main/LICENSE" 23 | enabled: true 24 | -------------------------------------------------------------------------------- /console/rsbuild.config.mjs: -------------------------------------------------------------------------------- 1 | import { rsbuildConfig } from "@halo-dev/ui-plugin-bundler-kit"; 2 | import Icons from "unplugin-icons/rspack"; 3 | import { UnoCSSRspackPlugin } from "@unocss/webpack/rspack"; 4 | 5 | const OUT_DIR_PROD = "../src/main/resources/console"; 6 | const OUT_DIR_DEV = "../build/resources/main/console"; 7 | 8 | export default rsbuildConfig({ 9 | rsbuild: ({ envMode }) => { 10 | const isProduction = envMode === "production"; 11 | const outDir = isProduction ? OUT_DIR_PROD : OUT_DIR_DEV; 12 | 13 | return { 14 | output: { 15 | distPath: { 16 | root: outDir, 17 | }, 18 | }, 19 | tools: { 20 | rspack: { 21 | plugins: [Icons({ compiler: "vue3" }), UnoCSSRspackPlugin()], 22 | }, 23 | }, 24 | }; 25 | }, 26 | }); 27 | -------------------------------------------------------------------------------- /src/main/java/run/halo/links/vo/LinkGroupVo.java: -------------------------------------------------------------------------------- 1 | package run.halo.links.vo; 2 | 3 | import lombok.Builder; 4 | import lombok.Value; 5 | import lombok.With; 6 | import run.halo.app.extension.MetadataOperator; 7 | import run.halo.app.theme.finders.vo.ExtensionVoOperator; 8 | import run.halo.links.LinkGroup; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | * @author guqing 14 | * @since 2.0.0 15 | */ 16 | @Value 17 | @Builder 18 | public class LinkGroupVo implements ExtensionVoOperator { 19 | 20 | MetadataOperator metadata; 21 | 22 | LinkGroup.LinkGroupSpec spec; 23 | 24 | @With 25 | List links; 26 | 27 | public static LinkGroupVo from(LinkGroup linkGroup) { 28 | return LinkGroupVo.builder() 29 | .metadata(linkGroup.getMetadata()) 30 | .spec(linkGroup.getSpec()) 31 | .links(List.of()) 32 | .build(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/run/halo/links/Link.java: -------------------------------------------------------------------------------- 1 | package run.halo.links; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.Data; 5 | import lombok.EqualsAndHashCode; 6 | import run.halo.app.extension.AbstractExtension; 7 | import run.halo.app.extension.GVK; 8 | 9 | /** 10 | * @author guqing 11 | * @author ryanwang 12 | * @since 2.0.0 13 | */ 14 | @Data 15 | @EqualsAndHashCode(callSuper = true) 16 | @GVK(group = "core.halo.run", version = "v1alpha1", 17 | kind = "Link", plural = "links", singular = "link") 18 | public class Link extends AbstractExtension { 19 | 20 | private LinkSpec spec; 21 | 22 | @Data 23 | public static class LinkSpec { 24 | @Schema(required = true) 25 | private String url; 26 | 27 | @Schema(required = true) 28 | private String displayName; 29 | 30 | private String logo; 31 | 32 | private String description; 33 | 34 | private Integer priority; 35 | 36 | private String groupName; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /console/src/api/generated/models/link-group-spec.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Halo 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 2.17.2 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | 16 | 17 | /** 18 | * 19 | * @export 20 | * @interface LinkGroupSpec 21 | */ 22 | export interface LinkGroupSpec { 23 | /** 24 | * 25 | * @type {string} 26 | * @memberof LinkGroupSpec 27 | */ 28 | 'displayName': string; 29 | /** 30 | * Names of links below this group. 31 | * @type {Array} 32 | * @memberof LinkGroupSpec 33 | * @deprecated 34 | */ 35 | 'links'?: Array; 36 | /** 37 | * 38 | * @type {number} 39 | * @memberof LinkGroupSpec 40 | */ 41 | 'priority'?: number; 42 | } 43 | 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Maven 2 | target/ 3 | logs/ 4 | !.mvn/wrapper/maven-wrapper.jar 5 | 6 | ### Gradle 7 | .gradle 8 | /build/ 9 | /out/ 10 | !gradle/wrapper/gradle-wrapper.jar 11 | bin/ 12 | 13 | ### STS ### 14 | .apt_generated 15 | .classpath 16 | .factorypath 17 | .project 18 | .settings 19 | .springBeans 20 | .sts4-cache 21 | 22 | ### IntelliJ IDEA ### 23 | .idea 24 | *.iws 25 | *.iml 26 | *.ipr 27 | log/ 28 | 29 | ### NetBeans ### 30 | nbproject/private/ 31 | build/ 32 | nbbuild/ 33 | dist/ 34 | nbdist/ 35 | .nb-gradle/ 36 | 37 | ### Mac 38 | .DS_Store 39 | */.DS_Store 40 | 41 | ### VS Code ### 42 | *.project 43 | *.factorypath 44 | 45 | ### Compiled class file 46 | *.class 47 | 48 | ### Log file 49 | *.log 50 | 51 | ### BlueJ files 52 | *.ctxt 53 | 54 | ### Mobile Tools for Java (J2ME) 55 | .mtj.tmp/ 56 | 57 | ### Package Files 58 | *.war 59 | *.nar 60 | *.ear 61 | *.zip 62 | *.tar.gz 63 | *.rar 64 | 65 | ### VSCode 66 | .vscode 67 | 68 | ### Local file 69 | application-local.yml 70 | application-local.yaml 71 | application-local.properties 72 | 73 | /workplace/ 74 | /src/main/resources/console/ 75 | -------------------------------------------------------------------------------- /console/src/api/generated/models/link-detail-dto.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Halo 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 2.17.2 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | 16 | 17 | /** 18 | * 19 | * @export 20 | * @interface LinkDetailDTO 21 | */ 22 | export interface LinkDetailDTO { 23 | /** 24 | * 25 | * @type {string} 26 | * @memberof LinkDetailDTO 27 | */ 28 | 'description'?: string; 29 | /** 30 | * 31 | * @type {string} 32 | * @memberof LinkDetailDTO 33 | */ 34 | 'icon'?: string; 35 | /** 36 | * 37 | * @type {string} 38 | * @memberof LinkDetailDTO 39 | */ 40 | 'image'?: string; 41 | /** 42 | * 43 | * @type {string} 44 | * @memberof LinkDetailDTO 45 | */ 46 | 'title': string; 47 | } 48 | 49 | -------------------------------------------------------------------------------- /console/src/api/generated/models/remove-operation.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Halo 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 2.17.2 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | 16 | 17 | /** 18 | * 19 | * @export 20 | * @interface RemoveOperation 21 | */ 22 | export interface RemoveOperation { 23 | /** 24 | * 25 | * @type {string} 26 | * @memberof RemoveOperation 27 | */ 28 | 'op': RemoveOperationOpEnum; 29 | /** 30 | * A JSON Pointer path pointing to the location to move/copy from. 31 | * @type {string} 32 | * @memberof RemoveOperation 33 | */ 34 | 'path': string; 35 | } 36 | 37 | export const RemoveOperationOpEnum = { 38 | Remove: 'remove' 39 | } as const; 40 | 41 | export type RemoveOperationOpEnum = typeof RemoveOperationOpEnum[keyof typeof RemoveOperationOpEnum]; 42 | 43 | 44 | -------------------------------------------------------------------------------- /console/src/api/generated/.openapi-generator-ignore: -------------------------------------------------------------------------------- 1 | # OpenAPI Generator Ignore 2 | # Generated by openapi-generator https://github.com/openapitools/openapi-generator 3 | 4 | # Use this file to prevent files from being overwritten by the generator. 5 | # The patterns follow closely to .gitignore or .dockerignore. 6 | 7 | # As an example, the C# client generator defines ApiClient.cs. 8 | # You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: 9 | #ApiClient.cs 10 | 11 | # You can match any string of characters against a directory, file or extension with a single asterisk (*): 12 | #foo/*/qux 13 | # The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux 14 | 15 | # You can recursively match patterns against a directory, file or extension with a double asterisk (**): 16 | #foo/**/qux 17 | # This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux 18 | 19 | # You can also negate patterns with an exclamation (!). 20 | # For example, you can ignore all files in a docs folder with the file extension .md: 21 | #docs/*.md 22 | # Then explicitly reverse the ignore rule for a single file: 23 | #!docs/README.md 24 | -------------------------------------------------------------------------------- /src/main/resources/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/java/run/halo/links/LinkGroup.java: -------------------------------------------------------------------------------- 1 | package run.halo.links; 2 | 3 | import io.swagger.v3.oas.annotations.media.ArraySchema; 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | import java.util.LinkedHashSet; 6 | import lombok.Data; 7 | import lombok.EqualsAndHashCode; 8 | import run.halo.app.extension.AbstractExtension; 9 | import run.halo.app.extension.GVK; 10 | 11 | /** 12 | * @author guqing 13 | * @author ryanwang 14 | * @since 2.0.0 15 | */ 16 | @Data 17 | @EqualsAndHashCode(callSuper = true) 18 | @GVK(group = "core.halo.run", version = "v1alpha1", kind = "LinkGroup", plural = "linkgroups", singular = "linkgroup") 19 | public class LinkGroup extends AbstractExtension { 20 | 21 | private LinkGroupSpec spec; 22 | 23 | @Data 24 | public static class LinkGroupSpec { 25 | @Schema(required = true) 26 | private String displayName; 27 | 28 | private Integer priority; 29 | 30 | @Deprecated(since = "1.2.0", forRemoval = true) 31 | @Schema(description = "Names of links below this group.") 32 | @ArraySchema(arraySchema = @Schema(description = "Links of this group."), schema = @Schema(description = "Name of link.")) 33 | private LinkedHashSet links; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /console/src/api/generated/models/add-operation.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Halo 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 2.17.2 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | 16 | 17 | /** 18 | * 19 | * @export 20 | * @interface AddOperation 21 | */ 22 | export interface AddOperation { 23 | /** 24 | * 25 | * @type {string} 26 | * @memberof AddOperation 27 | */ 28 | 'op': AddOperationOpEnum; 29 | /** 30 | * A JSON Pointer path pointing to the location to move/copy from. 31 | * @type {string} 32 | * @memberof AddOperation 33 | */ 34 | 'path': string; 35 | /** 36 | * Value can be any JSON value 37 | * @type {any} 38 | * @memberof AddOperation 39 | */ 40 | 'value': any; 41 | } 42 | 43 | export const AddOperationOpEnum = { 44 | Add: 'add' 45 | } as const; 46 | 47 | export type AddOperationOpEnum = typeof AddOperationOpEnum[keyof typeof AddOperationOpEnum]; 48 | 49 | 50 | -------------------------------------------------------------------------------- /console/src/api/generated/models/test-operation.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Halo 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 2.17.2 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | 16 | 17 | /** 18 | * 19 | * @export 20 | * @interface TestOperation 21 | */ 22 | export interface TestOperation { 23 | /** 24 | * 25 | * @type {string} 26 | * @memberof TestOperation 27 | */ 28 | 'op': TestOperationOpEnum; 29 | /** 30 | * A JSON Pointer path pointing to the location to move/copy from. 31 | * @type {string} 32 | * @memberof TestOperation 33 | */ 34 | 'path': string; 35 | /** 36 | * Value can be any JSON value 37 | * @type {any} 38 | * @memberof TestOperation 39 | */ 40 | 'value': any; 41 | } 42 | 43 | export const TestOperationOpEnum = { 44 | Test: 'test' 45 | } as const; 46 | 47 | export type TestOperationOpEnum = typeof TestOperationOpEnum[keyof typeof TestOperationOpEnum]; 48 | 49 | 50 | -------------------------------------------------------------------------------- /console/src/api/generated/models/link.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Halo 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 2.17.2 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | 16 | // May contain unused imports in some cases 17 | // @ts-ignore 18 | import type { LinkSpec } from './link-spec'; 19 | // May contain unused imports in some cases 20 | // @ts-ignore 21 | import type { Metadata } from './metadata'; 22 | 23 | /** 24 | * 25 | * @export 26 | * @interface Link 27 | */ 28 | export interface Link { 29 | /** 30 | * 31 | * @type {string} 32 | * @memberof Link 33 | */ 34 | 'apiVersion': string; 35 | /** 36 | * 37 | * @type {string} 38 | * @memberof Link 39 | */ 40 | 'kind': string; 41 | /** 42 | * 43 | * @type {Metadata} 44 | * @memberof Link 45 | */ 46 | 'metadata': Metadata; 47 | /** 48 | * 49 | * @type {LinkSpec} 50 | * @memberof Link 51 | */ 52 | 'spec'?: LinkSpec; 53 | } 54 | 55 | -------------------------------------------------------------------------------- /src/main/resources/extensions/roleTemplate.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1alpha1 2 | kind: Role 3 | metadata: 4 | name: role-template-link-view 5 | labels: 6 | halo.run/role-template: "true" 7 | annotations: 8 | rbac.authorization.halo.run/module: "链接" 9 | rbac.authorization.halo.run/display-name: "链接查看" 10 | rbac.authorization.halo.run/ui-permissions: | 11 | ["plugin:links:view"] 12 | rules: 13 | - apiGroups: [ "core.halo.run" ] 14 | resources: [ "links", "linkgroups" ] 15 | verbs: [ "get", "list" ] 16 | - apiGroups: [ "api.plugin.halo.run" ] 17 | resources: [ "plugins/links" ] 18 | resourceNames: [ "PluginLinks" ] 19 | verbs: [ "get", "list" ] 20 | --- 21 | apiVersion: v1alpha1 22 | kind: Role 23 | metadata: 24 | name: role-template-link-manage 25 | labels: 26 | halo.run/role-template: "true" 27 | annotations: 28 | rbac.authorization.halo.run/module: "链接" 29 | rbac.authorization.halo.run/display-name: "链接管理" 30 | rbac.authorization.halo.run/ui-permissions: | 31 | ["plugin:links:manage"] 32 | rbac.authorization.halo.run/dependencies: | 33 | ["role-template-link-view"] 34 | rules: 35 | - apiGroups: [ "core.halo.run" ] 36 | resources: [ "links", "linkgroups" ] 37 | verbs: [ "create", "patch", "update", "delete", "deletecollection" ] -------------------------------------------------------------------------------- /console/src/api/generated/models/copy-operation.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Halo 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 2.17.2 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | 16 | 17 | /** 18 | * 19 | * @export 20 | * @interface CopyOperation 21 | */ 22 | export interface CopyOperation { 23 | /** 24 | * A JSON Pointer path pointing to the location to move/copy from. 25 | * @type {string} 26 | * @memberof CopyOperation 27 | */ 28 | 'from': string; 29 | /** 30 | * 31 | * @type {string} 32 | * @memberof CopyOperation 33 | */ 34 | 'op': CopyOperationOpEnum; 35 | /** 36 | * A JSON Pointer path pointing to the location to move/copy from. 37 | * @type {string} 38 | * @memberof CopyOperation 39 | */ 40 | 'path': string; 41 | } 42 | 43 | export const CopyOperationOpEnum = { 44 | Copy: 'copy' 45 | } as const; 46 | 47 | export type CopyOperationOpEnum = typeof CopyOperationOpEnum[keyof typeof CopyOperationOpEnum]; 48 | 49 | 50 | -------------------------------------------------------------------------------- /console/src/api/generated/models/move-operation.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Halo 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 2.17.2 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | 16 | 17 | /** 18 | * 19 | * @export 20 | * @interface MoveOperation 21 | */ 22 | export interface MoveOperation { 23 | /** 24 | * A JSON Pointer path pointing to the location to move/copy from. 25 | * @type {string} 26 | * @memberof MoveOperation 27 | */ 28 | 'from': string; 29 | /** 30 | * 31 | * @type {string} 32 | * @memberof MoveOperation 33 | */ 34 | 'op': MoveOperationOpEnum; 35 | /** 36 | * A JSON Pointer path pointing to the location to move/copy from. 37 | * @type {string} 38 | * @memberof MoveOperation 39 | */ 40 | 'path': string; 41 | } 42 | 43 | export const MoveOperationOpEnum = { 44 | Move: 'move' 45 | } as const; 46 | 47 | export type MoveOperationOpEnum = typeof MoveOperationOpEnum[keyof typeof MoveOperationOpEnum]; 48 | 49 | 50 | -------------------------------------------------------------------------------- /console/src/api/generated/models/replace-operation.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Halo 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 2.17.2 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | 16 | 17 | /** 18 | * 19 | * @export 20 | * @interface ReplaceOperation 21 | */ 22 | export interface ReplaceOperation { 23 | /** 24 | * 25 | * @type {string} 26 | * @memberof ReplaceOperation 27 | */ 28 | 'op': ReplaceOperationOpEnum; 29 | /** 30 | * A JSON Pointer path pointing to the location to move/copy from. 31 | * @type {string} 32 | * @memberof ReplaceOperation 33 | */ 34 | 'path': string; 35 | /** 36 | * Value can be any JSON value 37 | * @type {any} 38 | * @memberof ReplaceOperation 39 | */ 40 | 'value': any; 41 | } 42 | 43 | export const ReplaceOperationOpEnum = { 44 | Replace: 'replace' 45 | } as const; 46 | 47 | export type ReplaceOperationOpEnum = typeof ReplaceOperationOpEnum[keyof typeof ReplaceOperationOpEnum]; 48 | 49 | 50 | -------------------------------------------------------------------------------- /console/src/api/generated/models/link-spec.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Halo 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 2.17.2 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | 16 | 17 | /** 18 | * 19 | * @export 20 | * @interface LinkSpec 21 | */ 22 | export interface LinkSpec { 23 | /** 24 | * 25 | * @type {string} 26 | * @memberof LinkSpec 27 | */ 28 | 'description'?: string; 29 | /** 30 | * 31 | * @type {string} 32 | * @memberof LinkSpec 33 | */ 34 | 'displayName': string; 35 | /** 36 | * 37 | * @type {string} 38 | * @memberof LinkSpec 39 | */ 40 | 'groupName'?: string; 41 | /** 42 | * 43 | * @type {string} 44 | * @memberof LinkSpec 45 | */ 46 | 'logo'?: string; 47 | /** 48 | * 49 | * @type {number} 50 | * @memberof LinkSpec 51 | */ 52 | 'priority'?: number; 53 | /** 54 | * 55 | * @type {string} 56 | * @memberof LinkSpec 57 | */ 58 | 'url': string; 59 | } 60 | 61 | -------------------------------------------------------------------------------- /console/src/api/generated/models/link-group.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Halo 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 2.17.2 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | 16 | // May contain unused imports in some cases 17 | // @ts-ignore 18 | import type { LinkGroupSpec } from './link-group-spec'; 19 | // May contain unused imports in some cases 20 | // @ts-ignore 21 | import type { Metadata } from './metadata'; 22 | 23 | /** 24 | * 25 | * @export 26 | * @interface LinkGroup 27 | */ 28 | export interface LinkGroup { 29 | /** 30 | * 31 | * @type {string} 32 | * @memberof LinkGroup 33 | */ 34 | 'apiVersion': string; 35 | /** 36 | * 37 | * @type {string} 38 | * @memberof LinkGroup 39 | */ 40 | 'kind': string; 41 | /** 42 | * 43 | * @type {Metadata} 44 | * @memberof LinkGroup 45 | */ 46 | 'metadata': Metadata; 47 | /** 48 | * 49 | * @type {LinkGroupSpec} 50 | * @memberof LinkGroup 51 | */ 52 | 'spec'?: LinkGroupSpec; 53 | } 54 | 55 | -------------------------------------------------------------------------------- /console/src/api/generated/models/json-patch-inner.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Halo 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 2.17.2 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | 16 | // May contain unused imports in some cases 17 | // @ts-ignore 18 | import type { AddOperation } from './add-operation'; 19 | // May contain unused imports in some cases 20 | // @ts-ignore 21 | import type { CopyOperation } from './copy-operation'; 22 | // May contain unused imports in some cases 23 | // @ts-ignore 24 | import type { MoveOperation } from './move-operation'; 25 | // May contain unused imports in some cases 26 | // @ts-ignore 27 | import type { RemoveOperation } from './remove-operation'; 28 | // May contain unused imports in some cases 29 | // @ts-ignore 30 | import type { ReplaceOperation } from './replace-operation'; 31 | // May contain unused imports in some cases 32 | // @ts-ignore 33 | import type { TestOperation } from './test-operation'; 34 | 35 | /** 36 | * @type JsonPatchInner 37 | * @export 38 | */ 39 | export type JsonPatchInner = AddOperation | CopyOperation | MoveOperation | RemoveOperation | ReplaceOperation | TestOperation; 40 | 41 | 42 | -------------------------------------------------------------------------------- /console/src/index.ts: -------------------------------------------------------------------------------- 1 | import { definePlugin, type CommentSubjectRefProvider, type CommentSubjectRefResult } from "@halo-dev/console-shared"; 2 | import { defineAsyncComponent, markRaw } from "vue"; 3 | import RiLinksLine from "~icons/ri/links-line"; 4 | import "uno.css"; 5 | import { VLoading } from "@halo-dev/components"; 6 | 7 | export default definePlugin({ 8 | components: {}, 9 | routes: [ 10 | { 11 | parentName: "Root", 12 | route: { 13 | path: "/links", 14 | name: "Links", 15 | component: defineAsyncComponent({ 16 | loader: () => import("@/views/LinkList.vue"), 17 | loadingComponent: VLoading, 18 | }), 19 | meta: { 20 | permissions: ["plugin:links:view"], 21 | menu: { 22 | name: "链接", 23 | group: "content", 24 | icon: markRaw(RiLinksLine), 25 | }, 26 | }, 27 | }, 28 | }, 29 | ], 30 | extensionPoints: { 31 | "comment:subject-ref:create": (): CommentSubjectRefProvider[] => { 32 | return [ 33 | { 34 | kind: "Plugin", 35 | group: "plugin.halo.run", 36 | resolve: (): CommentSubjectRefResult => { 37 | return { 38 | label: "链接", 39 | title: "链接页面", 40 | externalUrl: "/links", 41 | route: { 42 | name: "Links", 43 | }, 44 | }; 45 | }, 46 | }, 47 | ]; 48 | }, 49 | }, 50 | }); 51 | -------------------------------------------------------------------------------- /src/main/java/run/halo/links/LinkCommentSubject.java: -------------------------------------------------------------------------------- 1 | package run.halo.links; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.springframework.stereotype.Component; 5 | import org.springframework.util.Assert; 6 | import reactor.core.publisher.Mono; 7 | import run.halo.app.content.comment.CommentSubject; 8 | import run.halo.app.core.extension.Plugin; 9 | import run.halo.app.extension.GroupVersionKind; 10 | import run.halo.app.extension.ReactiveExtensionClient; 11 | import run.halo.app.extension.Ref; 12 | import run.halo.app.plugin.PluginContext; 13 | 14 | /** 15 | * @author LIlGG 16 | */ 17 | @Component 18 | @RequiredArgsConstructor 19 | public class LinkCommentSubject implements CommentSubject { 20 | 21 | private final ReactiveExtensionClient client; 22 | 23 | private final PluginContext pluginContext; 24 | 25 | @Override 26 | public Mono get(String name) { 27 | return client.get(Plugin.class, name); 28 | } 29 | 30 | @Override 31 | public Mono getSubjectDisplay(String name) { 32 | return Mono.just(new SubjectDisplay("链接", "/links", "链接")); 33 | } 34 | 35 | @Override 36 | public boolean supports(Ref ref) { 37 | Assert.notNull(ref, "Subject ref must not be null."); 38 | GroupVersionKind groupVersionKind = new GroupVersionKind(ref.getGroup(), 39 | ref.getVersion(), ref.getKind() 40 | ); 41 | return GroupVersionKind.fromExtension(Plugin.class).equals( 42 | groupVersionKind) && pluginContext.getName().equals(ref.getName()); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /console/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "scripts": { 4 | "dev": "rsbuild build --env-mode development --watch", 5 | "build": "rsbuild build", 6 | "type-check": "vue-tsc --noEmit -p tsconfig.app.json --composite false", 7 | "lint": "eslint ./src --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore", 8 | "prettier": "prettier --write './src/**/*.{vue,js,jsx,ts,tsx,css,scss,json,yml,yaml,html}'" 9 | }, 10 | "dependencies": { 11 | "@halo-dev/api-client": "^2.21.1", 12 | "@halo-dev/components": "^2.21.0", 13 | "@halo-dev/console-shared": "^2.21.0", 14 | "@tanstack/vue-query": "^4.41.0", 15 | "@vueuse/core": "^10.11.1", 16 | "@vueuse/router": "^10.11.1", 17 | "axios": "^1.13.1", 18 | "dayjs": "^1.11.19", 19 | "lodash.clonedeep": "^4.5.0", 20 | "vue": "^3.5.22", 21 | "vue-draggable-plus": "^0.6.0", 22 | "vue-router": "^4.6.3", 23 | "yaml": "^2.8.1" 24 | }, 25 | "devDependencies": { 26 | "@halo-dev/ui-plugin-bundler-kit": "2.21.2", 27 | "@iconify/json": "^2.2.402", 28 | "@rsbuild/core": "^1.6.0", 29 | "@rushstack/eslint-patch": "^1.14.1", 30 | "@types/lodash.clonedeep": "^4.5.9", 31 | "@types/node": "^16.18.126", 32 | "@unocss/eslint-config": "^66.5.4", 33 | "@unocss/webpack": "^66.5.4", 34 | "@vue/eslint-config-prettier": "^7.1.0", 35 | "@vue/eslint-config-typescript": "^10.0.0", 36 | "@vue/tsconfig": "^0.1.3", 37 | "eslint": "^8.57.1", 38 | "eslint-plugin-vue": "^8.7.1", 39 | "npm-run-all": "^4.1.5", 40 | "prettier": "^2.8.8", 41 | "typescript": "~5.5.4", 42 | "unocss": "^66.5.4", 43 | "unplugin-icons": "^22.5.0", 44 | "vue-tsc": "^2.2.12" 45 | }, 46 | "packageManager": "pnpm@10.20.0" 47 | } 48 | -------------------------------------------------------------------------------- /console/src/api/generated/models/metadata.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Halo 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 2.17.2 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | 16 | 17 | /** 18 | * 19 | * @export 20 | * @interface Metadata 21 | */ 22 | export interface Metadata { 23 | /** 24 | * 25 | * @type {{ [key: string]: string; }} 26 | * @memberof Metadata 27 | */ 28 | 'annotations'?: { [key: string]: string; }; 29 | /** 30 | * 31 | * @type {string} 32 | * @memberof Metadata 33 | */ 34 | 'creationTimestamp'?: string | null; 35 | /** 36 | * 37 | * @type {string} 38 | * @memberof Metadata 39 | */ 40 | 'deletionTimestamp'?: string | null; 41 | /** 42 | * 43 | * @type {Array} 44 | * @memberof Metadata 45 | */ 46 | 'finalizers'?: Array | null; 47 | /** 48 | * The name field will be generated automatically according to the given generateName field 49 | * @type {string} 50 | * @memberof Metadata 51 | */ 52 | 'generateName'?: string; 53 | /** 54 | * 55 | * @type {{ [key: string]: string; }} 56 | * @memberof Metadata 57 | */ 58 | 'labels'?: { [key: string]: string; }; 59 | /** 60 | * Metadata name 61 | * @type {string} 62 | * @memberof Metadata 63 | */ 64 | 'name': string; 65 | /** 66 | * 67 | * @type {number} 68 | * @memberof Metadata 69 | */ 70 | 'version'?: number | null; 71 | } 72 | 73 | -------------------------------------------------------------------------------- /src/main/java/run/halo/links/LinkRequest.java: -------------------------------------------------------------------------------- 1 | package run.halo.links; 2 | 3 | import java.io.IOException; 4 | import java.util.Map; 5 | import java.util.Objects; 6 | import org.jsoup.Jsoup; 7 | import org.jsoup.nodes.Document; 8 | import org.jsoup.nodes.Element; 9 | import org.springframework.web.server.ServerErrorException; 10 | 11 | /** 12 | * @author LIlGG 13 | */ 14 | public class LinkRequest { 15 | 16 | private final static String USER_AGENT = 17 | "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) " 18 | + "Chrome/58.0.3029.110 Safari/537.3"; 19 | 20 | public static LinkDetailDTO getLinkDetail(String linkUrl) { 21 | Document document; 22 | try { 23 | document = Jsoup.connect(linkUrl) 24 | .followRedirects(true) 25 | .maxBodySize(1024 * 1024 * 20) 26 | .timeout(10000) 27 | .headers( 28 | Map.of( 29 | "User-Agent", USER_AGENT, 30 | "Referer", linkUrl, 31 | "Accept", "text/html,application/xhtml+xml,application/xml" 32 | ) 33 | ) 34 | .get(); 35 | } catch (IOException e) { 36 | throw new ServerErrorException("Failed to get link detail", e); 37 | } 38 | 39 | LinkDetailDTO linkDetailDTO = new LinkDetailDTO(); 40 | linkDetailDTO.setTitle(document.title()); 41 | linkDetailDTO.setDescription(document.select("meta[name=description]") 42 | .attr("content")); 43 | Element iconElement = document.selectFirst("link[rel=icon]"); 44 | if (Objects.nonNull(iconElement)) { 45 | linkDetailDTO.setIcon(iconElement.absUrl("href")); 46 | } 47 | Element imageElement = document.selectFirst("meta[property=og:image]"); 48 | if (Objects.nonNull(imageElement)) { 49 | linkDetailDTO.setImage(imageElement.absUrl("content")); 50 | } 51 | return linkDetailDTO; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /console/src/api/generated/git_push.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/ 3 | # 4 | # Usage example: /bin/sh ./git_push.sh wing328 openapi-petstore-perl "minor update" "gitlab.com" 5 | 6 | git_user_id=$1 7 | git_repo_id=$2 8 | release_note=$3 9 | git_host=$4 10 | 11 | if [ "$git_host" = "" ]; then 12 | git_host="github.com" 13 | echo "[INFO] No command line input provided. Set \$git_host to $git_host" 14 | fi 15 | 16 | if [ "$git_user_id" = "" ]; then 17 | git_user_id="GIT_USER_ID" 18 | echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id" 19 | fi 20 | 21 | if [ "$git_repo_id" = "" ]; then 22 | git_repo_id="GIT_REPO_ID" 23 | echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id" 24 | fi 25 | 26 | if [ "$release_note" = "" ]; then 27 | release_note="Minor update" 28 | echo "[INFO] No command line input provided. Set \$release_note to $release_note" 29 | fi 30 | 31 | # Initialize the local directory as a Git repository 32 | git init 33 | 34 | # Adds the files in the local repository and stages them for commit. 35 | git add . 36 | 37 | # Commits the tracked changes and prepares them to be pushed to a remote repository. 38 | git commit -m "$release_note" 39 | 40 | # Sets the new remote 41 | git_remote=$(git remote) 42 | if [ "$git_remote" = "" ]; then # git remote not defined 43 | 44 | if [ "$GIT_TOKEN" = "" ]; then 45 | echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." 46 | git remote add origin https://${git_host}/${git_user_id}/${git_repo_id}.git 47 | else 48 | git remote add origin https://${git_user_id}:"${GIT_TOKEN}"@${git_host}/${git_user_id}/${git_repo_id}.git 49 | fi 50 | 51 | fi 52 | 53 | git pull origin master 54 | 55 | # Pushes (Forces) the changes in the local repository up to the remote repository 56 | echo "Git pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git" 57 | git push origin master 2>&1 | grep -v 'To https' 58 | -------------------------------------------------------------------------------- /console/src/api/generated/base.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Halo 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 2.17.2 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | 16 | import type { Configuration } from './configuration'; 17 | // Some imports not used depending on template conditions 18 | // @ts-ignore 19 | import type { AxiosPromise, AxiosInstance, RawAxiosRequestConfig } from 'axios'; 20 | import globalAxios from 'axios'; 21 | 22 | export const BASE_PATH = "http://localhost:31788".replace(/\/+$/, ""); 23 | 24 | /** 25 | * 26 | * @export 27 | */ 28 | export const COLLECTION_FORMATS = { 29 | csv: ",", 30 | ssv: " ", 31 | tsv: "\t", 32 | pipes: "|", 33 | }; 34 | 35 | /** 36 | * 37 | * @export 38 | * @interface RequestArgs 39 | */ 40 | export interface RequestArgs { 41 | url: string; 42 | options: RawAxiosRequestConfig; 43 | } 44 | 45 | /** 46 | * 47 | * @export 48 | * @class BaseAPI 49 | */ 50 | export class BaseAPI { 51 | protected configuration: Configuration | undefined; 52 | 53 | constructor(configuration?: Configuration, protected basePath: string = BASE_PATH, protected axios: AxiosInstance = globalAxios) { 54 | if (configuration) { 55 | this.configuration = configuration; 56 | this.basePath = configuration.basePath ?? basePath; 57 | } 58 | } 59 | }; 60 | 61 | /** 62 | * 63 | * @export 64 | * @class RequiredError 65 | * @extends {Error} 66 | */ 67 | export class RequiredError extends Error { 68 | constructor(public field: string, msg?: string) { 69 | super(msg); 70 | this.name = "RequiredError" 71 | } 72 | } 73 | 74 | interface ServerMap { 75 | [key: string]: { 76 | url: string, 77 | description: string, 78 | }[]; 79 | } 80 | 81 | /** 82 | * 83 | * @export 84 | */ 85 | export const operationServerMap: ServerMap = { 86 | } 87 | -------------------------------------------------------------------------------- /console/src/api/generated/models/link-list.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Halo 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 2.17.2 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | 16 | // May contain unused imports in some cases 17 | // @ts-ignore 18 | import type { Link } from './link'; 19 | 20 | /** 21 | * 22 | * @export 23 | * @interface LinkList 24 | */ 25 | export interface LinkList { 26 | /** 27 | * Indicates whether current page is the first page. 28 | * @type {boolean} 29 | * @memberof LinkList 30 | */ 31 | 'first': boolean; 32 | /** 33 | * Indicates whether current page has previous page. 34 | * @type {boolean} 35 | * @memberof LinkList 36 | */ 37 | 'hasNext': boolean; 38 | /** 39 | * Indicates whether current page has previous page. 40 | * @type {boolean} 41 | * @memberof LinkList 42 | */ 43 | 'hasPrevious': boolean; 44 | /** 45 | * A chunk of items. 46 | * @type {Array} 47 | * @memberof LinkList 48 | */ 49 | 'items': Array; 50 | /** 51 | * Indicates whether current page is the last page. 52 | * @type {boolean} 53 | * @memberof LinkList 54 | */ 55 | 'last': boolean; 56 | /** 57 | * Page number, starts from 1. If not set or equal to 0, it means no pagination. 58 | * @type {number} 59 | * @memberof LinkList 60 | */ 61 | 'page': number; 62 | /** 63 | * Size of each page. If not set or equal to 0, it means no pagination. 64 | * @type {number} 65 | * @memberof LinkList 66 | */ 67 | 'size': number; 68 | /** 69 | * Total elements. 70 | * @type {number} 71 | * @memberof LinkList 72 | */ 73 | 'total': number; 74 | /** 75 | * Indicates total pages. 76 | * @type {number} 77 | * @memberof LinkList 78 | */ 79 | 'totalPages': number; 80 | } 81 | 82 | -------------------------------------------------------------------------------- /console/src/api/generated/models/link-group-list.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Halo 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 2.17.2 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | 16 | // May contain unused imports in some cases 17 | // @ts-ignore 18 | import type { LinkGroup } from './link-group'; 19 | 20 | /** 21 | * 22 | * @export 23 | * @interface LinkGroupList 24 | */ 25 | export interface LinkGroupList { 26 | /** 27 | * Indicates whether current page is the first page. 28 | * @type {boolean} 29 | * @memberof LinkGroupList 30 | */ 31 | 'first': boolean; 32 | /** 33 | * Indicates whether current page has previous page. 34 | * @type {boolean} 35 | * @memberof LinkGroupList 36 | */ 37 | 'hasNext': boolean; 38 | /** 39 | * Indicates whether current page has previous page. 40 | * @type {boolean} 41 | * @memberof LinkGroupList 42 | */ 43 | 'hasPrevious': boolean; 44 | /** 45 | * A chunk of items. 46 | * @type {Array} 47 | * @memberof LinkGroupList 48 | */ 49 | 'items': Array; 50 | /** 51 | * Indicates whether current page is the last page. 52 | * @type {boolean} 53 | * @memberof LinkGroupList 54 | */ 55 | 'last': boolean; 56 | /** 57 | * Page number, starts from 1. If not set or equal to 0, it means no pagination. 58 | * @type {number} 59 | * @memberof LinkGroupList 60 | */ 61 | 'page': number; 62 | /** 63 | * Size of each page. If not set or equal to 0, it means no pagination. 64 | * @type {number} 65 | * @memberof LinkGroupList 66 | */ 67 | 'size': number; 68 | /** 69 | * Total elements. 70 | * @type {number} 71 | * @memberof LinkGroupList 72 | */ 73 | 'total': number; 74 | /** 75 | * Indicates total pages. 76 | * @type {number} 77 | * @memberof LinkGroupList 78 | */ 79 | 'totalPages': number; 80 | } 81 | 82 | -------------------------------------------------------------------------------- /console/src/composables/use-link.ts: -------------------------------------------------------------------------------- 1 | import { linksConsoleApiClient, linksCoreApiClient } from "@/api"; 2 | import { Link, LinkGroup } from "@/api/generated"; 3 | import { useQuery } from "@tanstack/vue-query"; 4 | import { ref, type Ref } from "vue"; 5 | 6 | export function useLinkFetch(page: Ref, size: Ref, keyword?: Ref, group?: Ref) { 7 | const total = ref(0); 8 | 9 | const links = ref([]); 10 | 11 | const { isLoading, refetch } = useQuery({ 12 | queryKey: ["links", page, size, group, keyword], 13 | queryFn: async () => { 14 | const { data } = await linksConsoleApiClient.link.listLinks({ 15 | page: page.value, 16 | size: size.value, 17 | keyword: keyword?.value, 18 | groupName: group?.value, 19 | sort: ["spec.priority,asc"], 20 | }); 21 | 22 | total.value = data.total; 23 | 24 | return data.items; 25 | }, 26 | refetchOnWindowFocus: false, 27 | refetchInterval(data) { 28 | const deletingLinks = data?.filter((link) => !!link.metadata.deletionTimestamp); 29 | return deletingLinks?.length ? 1000 : false; 30 | }, 31 | onSuccess(data) { 32 | links.value = data; 33 | }, 34 | }); 35 | 36 | return { 37 | links, 38 | isLoading, 39 | refetch, 40 | total, 41 | }; 42 | } 43 | 44 | export function useLinkGroupFetch() { 45 | const groups = ref([]); 46 | 47 | const { isLoading, refetch } = useQuery({ 48 | queryKey: ["link-groups"], 49 | queryFn: async () => { 50 | const { data } = await linksCoreApiClient.group.listLinkGroup(); 51 | 52 | return data.items 53 | .map((group) => { 54 | if (group.spec) { 55 | group.spec.priority = group.spec.priority || 0; 56 | } 57 | return group; 58 | }) 59 | .sort((a, b) => { 60 | return (a.spec?.priority || 0) - (b.spec?.priority || 0); 61 | }); 62 | }, 63 | refetchOnWindowFocus: false, 64 | refetchInterval(data) { 65 | const hasDeletingData = data?.some((group) => { 66 | return !!group.metadata.deletionTimestamp; 67 | }); 68 | return hasDeletingData ? 1000 : false; 69 | }, 70 | onSuccess(data) { 71 | groups.value = data; 72 | }, 73 | }); 74 | 75 | return { 76 | groups, 77 | isLoading, 78 | refetch, 79 | }; 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/run/halo/links/LinkPlugin.java: -------------------------------------------------------------------------------- 1 | package run.halo.links; 2 | 3 | import static run.halo.app.extension.index.IndexAttributeFactory.simpleAttribute; 4 | 5 | import org.apache.commons.lang3.StringUtils; 6 | import org.springframework.stereotype.Component; 7 | import run.halo.app.extension.Scheme; 8 | import run.halo.app.extension.SchemeManager; 9 | import run.halo.app.extension.index.IndexSpec; 10 | import run.halo.app.plugin.BasePlugin; 11 | import run.halo.app.plugin.PluginContext; 12 | 13 | /** 14 | * @author guqing 15 | * @since 2.0.0 16 | */ 17 | @Component 18 | public class LinkPlugin extends BasePlugin { 19 | 20 | private final SchemeManager schemeManager; 21 | 22 | public LinkPlugin(PluginContext pluginContext, SchemeManager schemeManager) { 23 | super(pluginContext); 24 | this.schemeManager = schemeManager; 25 | } 26 | 27 | @Override 28 | public void start() { 29 | schemeManager.register(Link.class, indexSpecs -> { 30 | indexSpecs.add(new IndexSpec() 31 | .setName("spec.displayName") 32 | .setIndexFunc(simpleAttribute(Link.class, link -> link.getSpec().getDisplayName())) 33 | ); 34 | indexSpecs.add(new IndexSpec() 35 | .setName("spec.description") 36 | .setIndexFunc(simpleAttribute(Link.class, link -> link.getSpec().getDescription())) 37 | ); 38 | indexSpecs.add(new IndexSpec() 39 | .setName("spec.url") 40 | .setIndexFunc(simpleAttribute(Link.class, link -> link.getSpec().getUrl())) 41 | ); 42 | indexSpecs.add(new IndexSpec() 43 | .setName("spec.groupName") 44 | .setIndexFunc(simpleAttribute(Link.class, link -> { 45 | var group = link.getSpec().getGroupName(); 46 | return StringUtils.isBlank(group) ? null : group; 47 | })) 48 | ); 49 | indexSpecs.add(new IndexSpec() 50 | .setName("spec.priority") 51 | .setIndexFunc(simpleAttribute(Link.class, link -> String.valueOf(link.getSpec().getPriority()))) 52 | ); 53 | }); 54 | schemeManager.register(LinkGroup.class, indexSpecs -> { 55 | indexSpecs.add(new IndexSpec() 56 | .setName("spec.priority") 57 | .setIndexFunc(simpleAttribute(LinkGroup.class, group -> String.valueOf(group.getSpec().getPriority()))) 58 | ); 59 | }); 60 | } 61 | 62 | @Override 63 | public void stop() { 64 | schemeManager.unregister(Scheme.buildFromType(Link.class)); 65 | schemeManager.unregister(Scheme.buildFromType(LinkGroup.class)); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /src/main/java/run/halo/links/finders/impl/LinkFinderImpl.java: -------------------------------------------------------------------------------- 1 | package run.halo.links.finders.impl; 2 | 3 | import static org.springframework.data.domain.Sort.Order.asc; 4 | import static run.halo.app.extension.index.query.QueryFactory.*; 5 | 6 | import org.springframework.data.domain.Sort; 7 | import reactor.core.publisher.Flux; 8 | import reactor.core.publisher.Mono; 9 | import run.halo.app.extension.ListOptions; 10 | import run.halo.app.extension.Metadata; 11 | import run.halo.app.extension.ReactiveExtensionClient; 12 | import run.halo.app.extension.router.selector.FieldSelector; 13 | import run.halo.app.theme.finders.Finder; 14 | import run.halo.links.Link; 15 | import run.halo.links.LinkGroup; 16 | import run.halo.links.finders.LinkFinder; 17 | import run.halo.links.vo.LinkGroupVo; 18 | import run.halo.links.vo.LinkVo; 19 | 20 | /** 21 | * A default implementation for {@link LinkFinder}. 22 | * 23 | * @author guqing 24 | * @author ryanwang 25 | */ 26 | @Finder("linkFinder") 27 | public class LinkFinderImpl implements LinkFinder { 28 | static final String UNGROUPED_NAME = "ungrouped"; 29 | private final ReactiveExtensionClient client; 30 | 31 | public LinkFinderImpl(ReactiveExtensionClient client) { 32 | this.client = client; 33 | } 34 | 35 | @Override 36 | public Flux listBy(String groupName) { 37 | var listOptions = new ListOptions(); 38 | var query = isNull("metadata.deletionTimestamp"); 39 | if (UNGROUPED_NAME.equals(groupName)) { 40 | query = and(query, isNull("spec.groupName")); 41 | } else { 42 | query = and(query, equal("spec.groupName", groupName)); 43 | } 44 | listOptions.setFieldSelector(FieldSelector.of(query)); 45 | return client.listAll(Link.class, listOptions, defaultLinkSort()) 46 | .map(LinkVo::from); 47 | } 48 | 49 | @Override 50 | public Flux groupBy() { 51 | return client.listAll(LinkGroup.class, new ListOptions(), defaultGroupSort()) 52 | .map(LinkGroupVo::from) 53 | .concatMap(group -> listBy(group.getMetadata().getName()) 54 | .collectList() 55 | .map(group::withLinks) 56 | .defaultIfEmpty(group) 57 | ) 58 | .mergeWith(Mono.defer(() -> listBy(UNGROUPED_NAME) 59 | .collectList() 60 | // do not return ungrouped group if no links 61 | .filter(links -> !links.isEmpty()) 62 | .flatMap(links -> ungrouped() 63 | .map(LinkGroupVo::from) 64 | .map(group -> group.withLinks(links)) 65 | ) 66 | )); 67 | } 68 | 69 | Mono ungrouped() { 70 | LinkGroup linkGroup = new LinkGroup(); 71 | linkGroup.setMetadata(new Metadata()); 72 | linkGroup.getMetadata().setName(UNGROUPED_NAME); 73 | linkGroup.setSpec(new LinkGroup.LinkGroupSpec()); 74 | linkGroup.getSpec().setDisplayName(""); 75 | linkGroup.getSpec().setPriority(0); 76 | return Mono.just(linkGroup); 77 | } 78 | 79 | static Sort defaultGroupSort() { 80 | return Sort.by(asc("spec.priority"), 81 | asc("metadata.creationTimestamp"), 82 | asc("metadata.name") 83 | ); 84 | } 85 | 86 | static Sort defaultLinkSort() { 87 | return Sort.by(asc("spec.priority"), 88 | asc("metadata.creationTimestamp"), 89 | asc("metadata.name") 90 | ); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /console/src/api/generated/configuration.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Halo 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 2.17.2 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | 16 | export interface ConfigurationParameters { 17 | apiKey?: string | Promise | ((name: string) => string) | ((name: string) => Promise); 18 | username?: string; 19 | password?: string; 20 | accessToken?: string | Promise | ((name?: string, scopes?: string[]) => string) | ((name?: string, scopes?: string[]) => Promise); 21 | basePath?: string; 22 | serverIndex?: number; 23 | baseOptions?: any; 24 | formDataCtor?: new () => any; 25 | } 26 | 27 | export class Configuration { 28 | /** 29 | * parameter for apiKey security 30 | * @param name security name 31 | * @memberof Configuration 32 | */ 33 | apiKey?: string | Promise | ((name: string) => string) | ((name: string) => Promise); 34 | /** 35 | * parameter for basic security 36 | * 37 | * @type {string} 38 | * @memberof Configuration 39 | */ 40 | username?: string; 41 | /** 42 | * parameter for basic security 43 | * 44 | * @type {string} 45 | * @memberof Configuration 46 | */ 47 | password?: string; 48 | /** 49 | * parameter for oauth2 security 50 | * @param name security name 51 | * @param scopes oauth2 scope 52 | * @memberof Configuration 53 | */ 54 | accessToken?: string | Promise | ((name?: string, scopes?: string[]) => string) | ((name?: string, scopes?: string[]) => Promise); 55 | /** 56 | * override base path 57 | * 58 | * @type {string} 59 | * @memberof Configuration 60 | */ 61 | basePath?: string; 62 | /** 63 | * override server index 64 | * 65 | * @type {number} 66 | * @memberof Configuration 67 | */ 68 | serverIndex?: number; 69 | /** 70 | * base options for axios calls 71 | * 72 | * @type {any} 73 | * @memberof Configuration 74 | */ 75 | baseOptions?: any; 76 | /** 77 | * The FormData constructor that will be used to create multipart form data 78 | * requests. You can inject this here so that execution environments that 79 | * do not support the FormData class can still run the generated client. 80 | * 81 | * @type {new () => FormData} 82 | */ 83 | formDataCtor?: new () => any; 84 | 85 | constructor(param: ConfigurationParameters = {}) { 86 | this.apiKey = param.apiKey; 87 | this.username = param.username; 88 | this.password = param.password; 89 | this.accessToken = param.accessToken; 90 | this.basePath = param.basePath; 91 | this.serverIndex = param.serverIndex; 92 | this.baseOptions = param.baseOptions; 93 | this.formDataCtor = param.formDataCtor; 94 | } 95 | 96 | /** 97 | * Check if the given MIME is a JSON MIME. 98 | * JSON MIME examples: 99 | * application/json 100 | * application/json; charset=UTF8 101 | * APPLICATION/JSON 102 | * application/vnd.company+json 103 | * @param mime - MIME (Multipurpose Internet Mail Extensions) 104 | * @return True if the given MIME is JSON, false otherwise. 105 | */ 106 | public isJsonMime(mime: string): boolean { 107 | const jsonMime: RegExp = new RegExp('^(application\/json|[^;/ \t]+\/[^;/ \t]+[+]json)[ \t]*(;.*)?$', 'i'); 108 | return mime !== null && (jsonMime.test(mime) || mime.toLowerCase() === 'application/json-patch+json'); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /console/src/components/GroupEditingModal.vue: -------------------------------------------------------------------------------- 1 | 92 | 144 | -------------------------------------------------------------------------------- /console/src/assets/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /console/src/api/generated/common.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Halo 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 2.17.2 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | 16 | import type { Configuration } from "./configuration"; 17 | import type { RequestArgs } from "./base"; 18 | import type { AxiosInstance, AxiosResponse } from 'axios'; 19 | import { RequiredError } from "./base"; 20 | 21 | /** 22 | * 23 | * @export 24 | */ 25 | export const DUMMY_BASE_URL = 'https://example.com' 26 | 27 | /** 28 | * 29 | * @throws {RequiredError} 30 | * @export 31 | */ 32 | export const assertParamExists = function (functionName: string, paramName: string, paramValue: unknown) { 33 | if (paramValue === null || paramValue === undefined) { 34 | throw new RequiredError(paramName, `Required parameter ${paramName} was null or undefined when calling ${functionName}.`); 35 | } 36 | } 37 | 38 | /** 39 | * 40 | * @export 41 | */ 42 | export const setApiKeyToObject = async function (object: any, keyParamName: string, configuration?: Configuration) { 43 | if (configuration && configuration.apiKey) { 44 | const localVarApiKeyValue = typeof configuration.apiKey === 'function' 45 | ? await configuration.apiKey(keyParamName) 46 | : await configuration.apiKey; 47 | object[keyParamName] = localVarApiKeyValue; 48 | } 49 | } 50 | 51 | /** 52 | * 53 | * @export 54 | */ 55 | export const setBasicAuthToObject = function (object: any, configuration?: Configuration) { 56 | if (configuration && (configuration.username || configuration.password)) { 57 | object["auth"] = { username: configuration.username, password: configuration.password }; 58 | } 59 | } 60 | 61 | /** 62 | * 63 | * @export 64 | */ 65 | export const setBearerAuthToObject = async function (object: any, configuration?: Configuration) { 66 | if (configuration && configuration.accessToken) { 67 | const accessToken = typeof configuration.accessToken === 'function' 68 | ? await configuration.accessToken() 69 | : await configuration.accessToken; 70 | object["Authorization"] = "Bearer " + accessToken; 71 | } 72 | } 73 | 74 | /** 75 | * 76 | * @export 77 | */ 78 | export const setOAuthToObject = async function (object: any, name: string, scopes: string[], configuration?: Configuration) { 79 | if (configuration && configuration.accessToken) { 80 | const localVarAccessTokenValue = typeof configuration.accessToken === 'function' 81 | ? await configuration.accessToken(name, scopes) 82 | : await configuration.accessToken; 83 | object["Authorization"] = "Bearer " + localVarAccessTokenValue; 84 | } 85 | } 86 | 87 | function setFlattenedQueryParams(urlSearchParams: URLSearchParams, parameter: any, key: string = ""): void { 88 | if (parameter == null) return; 89 | if (typeof parameter === "object") { 90 | if (Array.isArray(parameter)) { 91 | (parameter as any[]).forEach(item => setFlattenedQueryParams(urlSearchParams, item, key)); 92 | } 93 | else { 94 | Object.keys(parameter).forEach(currentKey => 95 | setFlattenedQueryParams(urlSearchParams, parameter[currentKey], `${key}${key !== '' ? '.' : ''}${currentKey}`) 96 | ); 97 | } 98 | } 99 | else { 100 | if (urlSearchParams.has(key)) { 101 | urlSearchParams.append(key, parameter); 102 | } 103 | else { 104 | urlSearchParams.set(key, parameter); 105 | } 106 | } 107 | } 108 | 109 | /** 110 | * 111 | * @export 112 | */ 113 | export const setSearchParams = function (url: URL, ...objects: any[]) { 114 | const searchParams = new URLSearchParams(url.search); 115 | setFlattenedQueryParams(searchParams, objects); 116 | url.search = searchParams.toString(); 117 | } 118 | 119 | /** 120 | * 121 | * @export 122 | */ 123 | export const serializeDataIfNeeded = function (value: any, requestOptions: any, configuration?: Configuration) { 124 | const nonString = typeof value !== 'string'; 125 | const needsSerialization = nonString && configuration && configuration.isJsonMime 126 | ? configuration.isJsonMime(requestOptions.headers['Content-Type']) 127 | : nonString; 128 | return needsSerialization 129 | ? JSON.stringify(value !== undefined ? value : {}) 130 | : (value || ""); 131 | } 132 | 133 | /** 134 | * 135 | * @export 136 | */ 137 | export const toPathString = function (url: URL) { 138 | return url.pathname + url.search + url.hash 139 | } 140 | 141 | /** 142 | * 143 | * @export 144 | */ 145 | export const createRequestFunction = function (axiosArgs: RequestArgs, globalAxios: AxiosInstance, BASE_PATH: string, configuration?: Configuration) { 146 | return >(axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => { 147 | const axiosRequestArgs = {...axiosArgs.options, url: (axios.defaults.baseURL ? '' : configuration?.basePath ?? basePath) + axiosArgs.url}; 148 | return axios.request(axiosRequestArgs); 149 | }; 150 | } 151 | -------------------------------------------------------------------------------- /console/src/components/GroupList.vue: -------------------------------------------------------------------------------- 1 | 97 | 171 | -------------------------------------------------------------------------------- /console/src/components/LinkEditingModal.vue: -------------------------------------------------------------------------------- 1 | 125 | 198 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # plugin-links 2 | 3 | Halo 2.0 的链接管理插件,支持在 Console 进行管理以及为主题端提供 `/links` 页面路由。 4 | 5 | ## 使用方式 6 | 7 | 1. 下载,目前提供以下两个下载方式: 8 | - GitHub Releases:访问 [Releases](https://github.com/halo-sigs/plugin-links/releases) 下载 Assets 中的 JAR 文件。 9 | - Halo 应用市场: 10 | 2. 安装,插件安装和更新方式可参考: 11 | 3. 安装完成之后,访问 Console 左侧的**链接**菜单项,即可进行管理。 12 | 4. 前台访问地址为 `/links`,需要注意的是,此插件需要主题提供模板(links.html)才能访问 `/links`。 13 | 14 | ## 开发环境 15 | 16 | ```bash 17 | git clone git@github.com:halo-sigs/plugin-links.git 18 | 19 | # 或者当你 fork 之后 20 | 21 | git clone git@github.com:{your_github_id}/plugin-links.git 22 | ``` 23 | 24 | ```bash 25 | cd path/to/plugin-links 26 | ``` 27 | 28 | ```bash 29 | # macOS / Linux 30 | ./gradlew pnpmInstall 31 | 32 | # Windows 33 | ./gradlew.bat pnpmInstall 34 | ``` 35 | 36 | ```bash 37 | # macOS / Linux 38 | ./gradlew build 39 | 40 | # Windows 41 | ./gradlew.bat build 42 | ``` 43 | 44 | 修改 Halo 配置文件: 45 | 46 | ```yaml 47 | halo: 48 | plugin: 49 | runtime-mode: development 50 | classes-directories: 51 | - "build/classes" 52 | - "build/resources" 53 | lib-directories: 54 | - "libs" 55 | fixedPluginPath: 56 | - "/path/to/plugin-links" 57 | ``` 58 | 59 | ## 主题适配 60 | 61 | 目前此插件为主题端提供了 `/links` 路由,模板为 `links.html`,也提供了 [Finder API](https://docs.halo.run/developer-guide/theme/finder-apis),可以将链接渲染到任何地方。 62 | 63 | ### 模板变量 64 | 65 | #### 路由信息 66 | 67 | - 模板路径:/templates/links.html 68 | - 访问路径:/links 69 | 70 | #### 变量 71 | 72 | groups 73 | 74 | ##### 变量类型 75 | 76 | List<[#LinkGroupVo](#linkgroupvo)> 77 | 78 | ##### 示例 79 | 80 | ```html 81 | 82 |

83 | 84 |
85 |
86 | 87 |
88 |
89 |
90 |

91 |

92 |
93 |
94 |
95 |
96 |
97 | ``` 98 | 99 | #### 变量 100 | 101 | linksTitle 102 | 103 | ##### 变量类型 104 | 105 | String 106 | 107 | ##### 示例 108 | 109 | ```html 110 |

111 | ``` 112 | 113 | ### Finder API 114 | 115 | #### listBy(group) 116 | 117 | ##### 描述 118 | 119 | 根据 group 获取链接。 120 | 121 | ##### 参数 122 | 123 | 1. `group:string` - 分组(LinkGroup)的唯一标识 `metadata.name`。 124 | 125 | ##### 返回值 126 | 127 | List<[#LinkVo](#linkvo)> 128 | 129 | ##### 示例 130 | 131 | ```html 132 | 133 | 134 |
135 |
136 | 137 |
138 |
139 |
140 |

141 |

142 |
143 |
144 |
145 |
146 |
147 | ``` 148 | 149 | #### groupBy() 150 | 151 | ##### 描述 152 | 153 | 获取所有分组,包含链接集合。 154 | 155 | ##### 参数 156 | 157 | 无 158 | 159 | ##### 返回值 160 | 161 | List<[#LinkGroupVo](#linkgroupvo)> 162 | 163 | ##### 示例 164 | 165 | ```html 166 | 167 |

168 | 169 |
170 |
171 | 172 |
173 |
174 |
175 |

176 |

177 |
178 |
179 |
180 |
181 |
182 | ``` 183 | 184 | ### 评论适配 185 | 186 | 主题开发者可以参考 [自定义标签](https://docs.halo.run/developer-guide/theme/template-tag/#halocomment),来为友情链接接入评论功能。 187 | 188 | #### 参数值 189 | 190 | group:plugin.halo.run 191 | 192 | kind: Plugin 193 | 194 | name: ${pluginName} 195 | 196 | #### 示例 197 | 198 | ```html 199 |
200 | 205 |
206 | ``` 207 | 208 | ### 类型定义 209 | 210 | #### LinkVo 211 | 212 | ```json 213 | { 214 | "metadata": { 215 | "name": "string", // 唯一标识 216 | "labels": { 217 | "additionalProp1": "string" 218 | }, 219 | "annotations": { 220 | "additionalProp1": "string" 221 | }, 222 | "creationTimestamp": "2022-11-20T13:06:38.512Z", // 创建时间 223 | }, 224 | "spec": { 225 | "url": "string", // 链接 226 | "displayName": "string", // 显示名称 227 | "description": "string", // 描述 228 | "logo": "string", // Logo 229 | "priority": 0, // 排序字段 230 | } 231 | } 232 | ``` 233 | 234 | #### LinkGroupVo 235 | 236 | ```json 237 | { 238 | "metadata": { 239 | "name": "string", // 唯一标识 240 | "labels": { 241 | "additionalProp1": "string" 242 | }, 243 | "annotations": { 244 | "additionalProp1": "string" 245 | }, 246 | "creationTimestamp": "2022-11-20T13:06:38.512Z", // 创建时间 247 | }, 248 | "spec": { 249 | "displayName": "string", // 显示名称 250 | "priority": 0, // 排序字段 251 | "links": [ // 链接集合,即 Link 的 metadata.name 的集合 252 | "string" 253 | ] 254 | }, 255 | "links": "List<#LinkVo>" // 链接集合 256 | } 257 | ``` 258 | 259 | ### Annotations 元数据适配 260 | 261 | 根据 Halo 的[元数据表单定义文档](https://docs.halo.run/developer-guide/annotations-form/)和[模型元数据文档](https://docs.halo.run/developer-guide/theme/annotations),Halo 支持为部分模型的表单添加元数据表单,此插件同样适配了此功能,如果你作为主题开发者,需要为链接或者链接分组添加额外的字段,可以参考上述文档并结合下面的 TargetRef 列表进行适配。 262 | 263 | | 对应模型 | group | kind | 264 | | ---------- | ---------------- | ---------- | 265 | | 链接 | core.halo.run | Link | 266 | | 链接分组 | core.halo.run | LinkGroup | 267 | -------------------------------------------------------------------------------- /src/main/java/run/halo/links/LinkRouter.java: -------------------------------------------------------------------------------- 1 | package run.halo.links; 2 | 3 | import static org.springdoc.core.fn.builders.apiresponse.Builder.responseBuilder; 4 | import static org.springdoc.core.fn.builders.parameter.Builder.parameterBuilder; 5 | import static org.springframework.data.domain.Sort.Order.asc; 6 | import static org.springframework.data.domain.Sort.Order.desc; 7 | import static org.springframework.web.reactive.function.server.RequestPredicates.GET; 8 | import static org.springframework.web.reactive.function.server.RouterFunctions.route; 9 | import static run.halo.app.extension.index.query.QueryFactory.and; 10 | import static run.halo.app.extension.index.query.QueryFactory.contains; 11 | import static run.halo.app.extension.index.query.QueryFactory.equal; 12 | import static run.halo.app.extension.index.query.QueryFactory.or; 13 | import static run.halo.app.extension.router.selector.SelectorUtil.labelAndFieldSelectorToListOptions; 14 | 15 | import java.util.List; 16 | import java.util.Map; 17 | 18 | import org.apache.commons.lang3.StringUtils; 19 | import org.springdoc.core.fn.builders.operation.Builder; 20 | import org.springdoc.webflux.core.fn.SpringdocRouteBuilder; 21 | import org.springframework.context.annotation.Bean; 22 | import org.springframework.data.domain.Sort; 23 | import org.springframework.stereotype.Component; 24 | import org.springframework.web.reactive.function.server.RequestPredicates; 25 | import org.springframework.web.reactive.function.server.RouterFunction; 26 | import org.springframework.web.reactive.function.server.ServerRequest; 27 | import org.springframework.web.reactive.function.server.ServerResponse; 28 | import org.springframework.web.server.ServerWebExchange; 29 | import org.springframework.web.server.ServerWebInputException; 30 | 31 | import io.swagger.v3.oas.annotations.enums.ParameterIn; 32 | import io.swagger.v3.oas.annotations.media.Schema; 33 | import lombok.RequiredArgsConstructor; 34 | import reactor.core.publisher.Mono; 35 | import reactor.core.scheduler.Schedulers; 36 | import run.halo.app.extension.ListOptions; 37 | import run.halo.app.extension.ListResult; 38 | import run.halo.app.extension.ReactiveExtensionClient; 39 | import run.halo.app.extension.router.SortableRequest; 40 | import run.halo.app.extension.router.selector.FieldSelector; 41 | import run.halo.app.infra.utils.PathUtils; 42 | import run.halo.app.plugin.PluginContext; 43 | import run.halo.app.plugin.ReactiveSettingFetcher; 44 | import run.halo.links.finders.LinkFinder; 45 | import run.halo.links.vo.LinkGroupVo; 46 | 47 | @Component 48 | @RequiredArgsConstructor 49 | public class LinkRouter { 50 | 51 | private final LinkFinder linkFinder; 52 | private final ReactiveExtensionClient client; 53 | private final String tag = "api.plugin.halo.run/v1alpha1/Link"; 54 | private final PluginContext pluginContext; 55 | private final ReactiveSettingFetcher settingFetcher; 56 | 57 | @Bean 58 | RouterFunction linkTemplateRoute() { 59 | return route(GET("/links"), 60 | request -> ServerResponse.ok().render("links", 61 | Map.of("groups", linkGroups(), 62 | "pluginName", pluginContext.getName(), 63 | "linksTitle", getLinkTitle()))); 64 | } 65 | 66 | @Bean 67 | RouterFunction linkRoute() { 68 | return SpringdocRouteBuilder.route() 69 | .nest(RequestPredicates.path("/apis/api.plugin.halo.run/v1alpha1/plugins/PluginLinks"), 70 | this::nested, 71 | builder -> builder.operationId("PluginLinksEndpoints") 72 | .description("Plugin links Endpoints").tag(tag) 73 | ) 74 | .build(); 75 | } 76 | 77 | RouterFunction nested() { 78 | return SpringdocRouteBuilder.route() 79 | .GET("links", this::listLinkByGroup, 80 | builder -> { 81 | builder.operationId("listLinks") 82 | .description("Lists link by query parameters") 83 | .tag(tag) 84 | .response(responseBuilder() 85 | .implementation(ListResult.generateGenericClass(Link.class))); 86 | LinkQuery.buildParameters(builder); 87 | } 88 | ) 89 | .GET("link-detail", this::getLinkDetail, builder -> { 90 | builder.operationId("GetLinkDetail") 91 | .description("Get link detail by id") 92 | .tag(tag) 93 | .parameter(parameterBuilder() 94 | .name("url") 95 | .description("Link url") 96 | .in(ParameterIn.QUERY) 97 | .implementation(String.class) 98 | .required(true) 99 | ) 100 | .response(responseBuilder().implementation(LinkDetailDTO.class)); 101 | }).build(); 102 | } 103 | 104 | private Mono getLinkDetail(ServerRequest request) { 105 | final var url = request.queryParam("url") 106 | .filter(PathUtils::isAbsoluteUri) 107 | .orElseThrow(() -> new ServerWebInputException("Invalid url.")); 108 | return Mono.fromSupplier(() -> LinkRequest.getLinkDetail(url)) 109 | .subscribeOn(Schedulers.boundedElastic()) 110 | .publishOn(Schedulers.parallel()) 111 | .flatMap(dto -> ServerResponse.ok().bodyValue(dto)); 112 | } 113 | 114 | Mono listLinkByGroup(ServerRequest request) { 115 | LinkQuery linkQuery = new LinkQuery(request.exchange()); 116 | return listLink(linkQuery) 117 | .flatMap(links -> ServerResponse.ok().bodyValue(links)); 118 | } 119 | 120 | private Mono> listLink(LinkQuery query) { 121 | return client.listBy(Link.class, query.toListOptions(), query.toPageRequest()); 122 | } 123 | 124 | static class LinkQuery extends SortableRequest { 125 | 126 | public LinkQuery(ServerWebExchange exchange) { 127 | super(exchange); 128 | } 129 | 130 | @Schema(description = "Keyword to search links under the group") 131 | public String getKeyword() { 132 | return queryParams.getFirst("keyword"); 133 | } 134 | 135 | @Schema(description = "Link group name") 136 | public String getGroupName() { 137 | return queryParams.getFirst("groupName"); 138 | } 139 | 140 | @Override 141 | public ListOptions toListOptions() { 142 | var listOptions = 143 | labelAndFieldSelectorToListOptions(getLabelSelector(), getFieldSelector()); 144 | var query = listOptions.getFieldSelector().query(); 145 | if (StringUtils.isNotBlank(getKeyword())) { 146 | query = and(query, or( 147 | contains("spec.displayName", getKeyword()), 148 | contains("spec.description", getKeyword()), 149 | contains("spec.url", getKeyword()) 150 | )); 151 | } 152 | 153 | if (StringUtils.isNotBlank(getGroupName())) { 154 | query = and(query, equal("spec.groupName", getGroupName())); 155 | } 156 | listOptions.setFieldSelector(FieldSelector.of(query)); 157 | return listOptions; 158 | } 159 | 160 | @Override 161 | public Sort getSort() { 162 | return super.getSort() 163 | .and(Sort.by(desc("metadata.creationTimestamp"), 164 | asc("metadata.name")) 165 | ); 166 | } 167 | 168 | public static void buildParameters(Builder builder) { 169 | builder.parameter(parameterBuilder() 170 | .name("keyword") 171 | .description("Keyword to search links under the group") 172 | .in(ParameterIn.QUERY) 173 | .implementation(String.class) 174 | .required(false) 175 | ) 176 | .parameter(parameterBuilder() 177 | .name("groupName") 178 | .description("Link group name") 179 | .in(ParameterIn.QUERY) 180 | .implementation(String.class) 181 | .required(false) 182 | ); 183 | SortableRequest.buildParameters(builder); 184 | } 185 | } 186 | 187 | private Mono> linkGroups() { 188 | return linkFinder.groupBy() 189 | .collectList(); 190 | } 191 | 192 | Mono getLinkTitle() { 193 | return this.settingFetcher.get("base") 194 | .map(setting -> setting.get("title").asText()) 195 | .defaultIfEmpty("链接"); 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # SPDX-License-Identifier: Apache-2.0 19 | # 20 | 21 | ############################################################################## 22 | # 23 | # Gradle start up script for POSIX generated by Gradle. 24 | # 25 | # Important for running: 26 | # 27 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 28 | # noncompliant, but you have some other compliant shell such as ksh or 29 | # bash, then to run this script, type that shell name before the whole 30 | # command line, like: 31 | # 32 | # ksh Gradle 33 | # 34 | # Busybox and similar reduced shells will NOT work, because this script 35 | # requires all of these POSIX shell features: 36 | # * functions; 37 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 38 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 39 | # * compound commands having a testable exit status, especially «case»; 40 | # * various built-in commands including «command», «set», and «ulimit». 41 | # 42 | # Important for patching: 43 | # 44 | # (2) This script targets any POSIX shell, so it avoids extensions provided 45 | # by Bash, Ksh, etc; in particular arrays are avoided. 46 | # 47 | # The "traditional" practice of packing multiple parameters into a 48 | # space-separated string is a well documented source of bugs and security 49 | # problems, so this is (mostly) avoided, by progressively accumulating 50 | # options in "$@", and eventually passing that to Java. 51 | # 52 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 53 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 54 | # see the in-line comments for details. 55 | # 56 | # There are tweaks for specific operating systems such as AIX, CygWin, 57 | # Darwin, MinGW, and NonStop. 58 | # 59 | # (3) This script is generated from the Groovy template 60 | # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 61 | # within the Gradle project. 62 | # 63 | # You can find Gradle at https://github.com/gradle/gradle/. 64 | # 65 | ############################################################################## 66 | 67 | # Attempt to set APP_HOME 68 | 69 | # Resolve links: $0 may be a link 70 | app_path=$0 71 | 72 | # Need this for daisy-chained symlinks. 73 | while 74 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 75 | [ -h "$app_path" ] 76 | do 77 | ls=$( ls -ld "$app_path" ) 78 | link=${ls#*' -> '} 79 | case $link in #( 80 | /*) app_path=$link ;; #( 81 | *) app_path=$APP_HOME$link ;; 82 | esac 83 | done 84 | 85 | # This is normally unused 86 | # shellcheck disable=SC2034 87 | APP_BASE_NAME=${0##*/} 88 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 89 | APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s 90 | ' "$PWD" ) || exit 91 | 92 | # Use the maximum available, or set MAX_FD != -1 to use that value. 93 | MAX_FD=maximum 94 | 95 | warn () { 96 | echo "$*" 97 | } >&2 98 | 99 | die () { 100 | echo 101 | echo "$*" 102 | echo 103 | exit 1 104 | } >&2 105 | 106 | # OS specific support (must be 'true' or 'false'). 107 | cygwin=false 108 | msys=false 109 | darwin=false 110 | nonstop=false 111 | case "$( uname )" in #( 112 | CYGWIN* ) cygwin=true ;; #( 113 | Darwin* ) darwin=true ;; #( 114 | MSYS* | MINGW* ) msys=true ;; #( 115 | NONSTOP* ) nonstop=true ;; 116 | esac 117 | 118 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 119 | 120 | 121 | # Determine the Java command to use to start the JVM. 122 | if [ -n "$JAVA_HOME" ] ; then 123 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 124 | # IBM's JDK on AIX uses strange locations for the executables 125 | JAVACMD=$JAVA_HOME/jre/sh/java 126 | else 127 | JAVACMD=$JAVA_HOME/bin/java 128 | fi 129 | if [ ! -x "$JAVACMD" ] ; then 130 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 131 | 132 | Please set the JAVA_HOME variable in your environment to match the 133 | location of your Java installation." 134 | fi 135 | else 136 | JAVACMD=java 137 | if ! command -v java >/dev/null 2>&1 138 | then 139 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 140 | 141 | Please set the JAVA_HOME variable in your environment to match the 142 | location of your Java installation." 143 | fi 144 | fi 145 | 146 | # Increase the maximum file descriptors if we can. 147 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 148 | case $MAX_FD in #( 149 | max*) 150 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 151 | # shellcheck disable=SC2039,SC3045 152 | MAX_FD=$( ulimit -H -n ) || 153 | warn "Could not query maximum file descriptor limit" 154 | esac 155 | case $MAX_FD in #( 156 | '' | soft) :;; #( 157 | *) 158 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 159 | # shellcheck disable=SC2039,SC3045 160 | ulimit -n "$MAX_FD" || 161 | warn "Could not set maximum file descriptor limit to $MAX_FD" 162 | esac 163 | fi 164 | 165 | # Collect all arguments for the java command, stacking in reverse order: 166 | # * args from the command line 167 | # * the main class name 168 | # * -classpath 169 | # * -D...appname settings 170 | # * --module-path (only if needed) 171 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 172 | 173 | # For Cygwin or MSYS, switch paths to Windows format before running java 174 | if "$cygwin" || "$msys" ; then 175 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 176 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 177 | 178 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 179 | 180 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 181 | for arg do 182 | if 183 | case $arg in #( 184 | -*) false ;; # don't mess with options #( 185 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 186 | [ -e "$t" ] ;; #( 187 | *) false ;; 188 | esac 189 | then 190 | arg=$( cygpath --path --ignore --mixed "$arg" ) 191 | fi 192 | # Roll the args list around exactly as many times as the number of 193 | # args, so each arg winds up back in the position where it started, but 194 | # possibly modified. 195 | # 196 | # NB: a `for` loop captures its iteration list before it begins, so 197 | # changing the positional parameters here affects neither the number of 198 | # iterations, nor the values presented in `arg`. 199 | shift # remove old arg 200 | set -- "$@" "$arg" # push replacement arg 201 | done 202 | fi 203 | 204 | 205 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 206 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 207 | 208 | # Collect all arguments for the java command: 209 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 210 | # and any embedded shellness will be escaped. 211 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 212 | # treated as '${Hostname}' itself on the command line. 213 | 214 | set -- \ 215 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 216 | -classpath "$CLASSPATH" \ 217 | org.gradle.wrapper.GradleWrapperMain \ 218 | "$@" 219 | 220 | # Stop when "xargs" is not available. 221 | if ! command -v xargs >/dev/null 2>&1 222 | then 223 | die "xargs is not available" 224 | fi 225 | 226 | # Use "xargs" to parse quoted args. 227 | # 228 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 229 | # 230 | # In Bash we could simply go: 231 | # 232 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 233 | # set -- "${ARGS[@]}" "$@" 234 | # 235 | # but POSIX shell has neither arrays nor command substitution, so instead we 236 | # post-process each arg (as a line of input to sed) to backslash-escape any 237 | # character that might be a shell metacharacter, then use eval to reverse 238 | # that process (while maintaining the separation between arguments), and wrap 239 | # the whole thing up as a single "set" statement. 240 | # 241 | # This will of course break if any of these variables contains a newline or 242 | # an unmatched quote. 243 | # 244 | 245 | eval "set -- $( 246 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 247 | xargs -n1 | 248 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 249 | tr '\n' ' ' 250 | )" '"$@"' 251 | 252 | exec "$JAVACMD" "$@" 253 | -------------------------------------------------------------------------------- /console/src/api/generated/api/api-plugin-halo-run-v1alpha1-link-api.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * Halo 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 2.17.2 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | 16 | import type { Configuration } from '../configuration'; 17 | import type { AxiosPromise, AxiosInstance, RawAxiosRequestConfig } from 'axios'; 18 | import globalAxios from 'axios'; 19 | // Some imports not used depending on template conditions 20 | // @ts-ignore 21 | import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObject, setBearerAuthToObject, setOAuthToObject, setSearchParams, serializeDataIfNeeded, toPathString, createRequestFunction } from '../common'; 22 | // @ts-ignore 23 | import { BASE_PATH, COLLECTION_FORMATS, type RequestArgs, BaseAPI, RequiredError, operationServerMap } from '../base'; 24 | // @ts-ignore 25 | import type { LinkDetailDTO } from '../models'; 26 | // @ts-ignore 27 | import type { LinkList } from '../models'; 28 | /** 29 | * ApiPluginHaloRunV1alpha1LinkApi - axios parameter creator 30 | * @export 31 | */ 32 | export const ApiPluginHaloRunV1alpha1LinkApiAxiosParamCreator = function (configuration?: Configuration) { 33 | return { 34 | /** 35 | * Get link detail by id 36 | * @param {string} url Link url 37 | * @param {*} [options] Override http request option. 38 | * @throws {RequiredError} 39 | */ 40 | getLinkDetail: async (url: string, options: RawAxiosRequestConfig = {}): Promise => { 41 | // verify required parameter 'url' is not null or undefined 42 | assertParamExists('getLinkDetail', 'url', url) 43 | const localVarPath = `/apis/api.plugin.halo.run/v1alpha1/plugins/PluginLinks/link-detail`; 44 | // use dummy base URL string because the URL constructor only accepts absolute URLs. 45 | const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); 46 | let baseOptions; 47 | if (configuration) { 48 | baseOptions = configuration.baseOptions; 49 | } 50 | 51 | const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; 52 | const localVarHeaderParameter = {} as any; 53 | const localVarQueryParameter = {} as any; 54 | 55 | // authentication basicAuth required 56 | // http basic authentication required 57 | setBasicAuthToObject(localVarRequestOptions, configuration) 58 | 59 | // authentication bearerAuth required 60 | // http bearer authentication required 61 | await setBearerAuthToObject(localVarHeaderParameter, configuration) 62 | 63 | if (url !== undefined) { 64 | localVarQueryParameter['url'] = url; 65 | } 66 | 67 | 68 | 69 | setSearchParams(localVarUrlObj, localVarQueryParameter); 70 | let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; 71 | localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; 72 | 73 | return { 74 | url: toPathString(localVarUrlObj), 75 | options: localVarRequestOptions, 76 | }; 77 | }, 78 | /** 79 | * Lists link by query parameters 80 | * @param {string} [keyword] Keyword to search links under the group 81 | * @param {string} [groupName] Link group name 82 | * @param {number} [page] Page number. Default is 0. 83 | * @param {number} [size] Size number. Default is 0. 84 | * @param {Array} [labelSelector] Label selector. e.g.: hidden!=true 85 | * @param {Array} [fieldSelector] Field selector. e.g.: metadata.name==halo 86 | * @param {Array} [sort] Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported. 87 | * @param {*} [options] Override http request option. 88 | * @throws {RequiredError} 89 | */ 90 | listLinks: async (keyword?: string, groupName?: string, page?: number, size?: number, labelSelector?: Array, fieldSelector?: Array, sort?: Array, options: RawAxiosRequestConfig = {}): Promise => { 91 | const localVarPath = `/apis/api.plugin.halo.run/v1alpha1/plugins/PluginLinks/links`; 92 | // use dummy base URL string because the URL constructor only accepts absolute URLs. 93 | const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); 94 | let baseOptions; 95 | if (configuration) { 96 | baseOptions = configuration.baseOptions; 97 | } 98 | 99 | const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; 100 | const localVarHeaderParameter = {} as any; 101 | const localVarQueryParameter = {} as any; 102 | 103 | // authentication basicAuth required 104 | // http basic authentication required 105 | setBasicAuthToObject(localVarRequestOptions, configuration) 106 | 107 | // authentication bearerAuth required 108 | // http bearer authentication required 109 | await setBearerAuthToObject(localVarHeaderParameter, configuration) 110 | 111 | if (keyword !== undefined) { 112 | localVarQueryParameter['keyword'] = keyword; 113 | } 114 | 115 | if (groupName !== undefined) { 116 | localVarQueryParameter['groupName'] = groupName; 117 | } 118 | 119 | if (page !== undefined) { 120 | localVarQueryParameter['page'] = page; 121 | } 122 | 123 | if (size !== undefined) { 124 | localVarQueryParameter['size'] = size; 125 | } 126 | 127 | if (labelSelector) { 128 | localVarQueryParameter['labelSelector'] = labelSelector; 129 | } 130 | 131 | if (fieldSelector) { 132 | localVarQueryParameter['fieldSelector'] = fieldSelector; 133 | } 134 | 135 | if (sort) { 136 | localVarQueryParameter['sort'] = sort; 137 | } 138 | 139 | 140 | 141 | setSearchParams(localVarUrlObj, localVarQueryParameter); 142 | let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; 143 | localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; 144 | 145 | return { 146 | url: toPathString(localVarUrlObj), 147 | options: localVarRequestOptions, 148 | }; 149 | }, 150 | } 151 | }; 152 | 153 | /** 154 | * ApiPluginHaloRunV1alpha1LinkApi - functional programming interface 155 | * @export 156 | */ 157 | export const ApiPluginHaloRunV1alpha1LinkApiFp = function(configuration?: Configuration) { 158 | const localVarAxiosParamCreator = ApiPluginHaloRunV1alpha1LinkApiAxiosParamCreator(configuration) 159 | return { 160 | /** 161 | * Get link detail by id 162 | * @param {string} url Link url 163 | * @param {*} [options] Override http request option. 164 | * @throws {RequiredError} 165 | */ 166 | async getLinkDetail(url: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { 167 | const localVarAxiosArgs = await localVarAxiosParamCreator.getLinkDetail(url, options); 168 | const localVarOperationServerIndex = configuration?.serverIndex ?? 0; 169 | const localVarOperationServerBasePath = operationServerMap['ApiPluginHaloRunV1alpha1LinkApi.getLinkDetail']?.[localVarOperationServerIndex]?.url; 170 | return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); 171 | }, 172 | /** 173 | * Lists link by query parameters 174 | * @param {string} [keyword] Keyword to search links under the group 175 | * @param {string} [groupName] Link group name 176 | * @param {number} [page] Page number. Default is 0. 177 | * @param {number} [size] Size number. Default is 0. 178 | * @param {Array} [labelSelector] Label selector. e.g.: hidden!=true 179 | * @param {Array} [fieldSelector] Field selector. e.g.: metadata.name==halo 180 | * @param {Array} [sort] Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported. 181 | * @param {*} [options] Override http request option. 182 | * @throws {RequiredError} 183 | */ 184 | async listLinks(keyword?: string, groupName?: string, page?: number, size?: number, labelSelector?: Array, fieldSelector?: Array, sort?: Array, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { 185 | const localVarAxiosArgs = await localVarAxiosParamCreator.listLinks(keyword, groupName, page, size, labelSelector, fieldSelector, sort, options); 186 | const localVarOperationServerIndex = configuration?.serverIndex ?? 0; 187 | const localVarOperationServerBasePath = operationServerMap['ApiPluginHaloRunV1alpha1LinkApi.listLinks']?.[localVarOperationServerIndex]?.url; 188 | return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); 189 | }, 190 | } 191 | }; 192 | 193 | /** 194 | * ApiPluginHaloRunV1alpha1LinkApi - factory interface 195 | * @export 196 | */ 197 | export const ApiPluginHaloRunV1alpha1LinkApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { 198 | const localVarFp = ApiPluginHaloRunV1alpha1LinkApiFp(configuration) 199 | return { 200 | /** 201 | * Get link detail by id 202 | * @param {ApiPluginHaloRunV1alpha1LinkApiGetLinkDetailRequest} requestParameters Request parameters. 203 | * @param {*} [options] Override http request option. 204 | * @throws {RequiredError} 205 | */ 206 | getLinkDetail(requestParameters: ApiPluginHaloRunV1alpha1LinkApiGetLinkDetailRequest, options?: RawAxiosRequestConfig): AxiosPromise { 207 | return localVarFp.getLinkDetail(requestParameters.url, options).then((request) => request(axios, basePath)); 208 | }, 209 | /** 210 | * Lists link by query parameters 211 | * @param {ApiPluginHaloRunV1alpha1LinkApiListLinksRequest} requestParameters Request parameters. 212 | * @param {*} [options] Override http request option. 213 | * @throws {RequiredError} 214 | */ 215 | listLinks(requestParameters: ApiPluginHaloRunV1alpha1LinkApiListLinksRequest = {}, options?: RawAxiosRequestConfig): AxiosPromise { 216 | return localVarFp.listLinks(requestParameters.keyword, requestParameters.groupName, requestParameters.page, requestParameters.size, requestParameters.labelSelector, requestParameters.fieldSelector, requestParameters.sort, options).then((request) => request(axios, basePath)); 217 | }, 218 | }; 219 | }; 220 | 221 | /** 222 | * Request parameters for getLinkDetail operation in ApiPluginHaloRunV1alpha1LinkApi. 223 | * @export 224 | * @interface ApiPluginHaloRunV1alpha1LinkApiGetLinkDetailRequest 225 | */ 226 | export interface ApiPluginHaloRunV1alpha1LinkApiGetLinkDetailRequest { 227 | /** 228 | * Link url 229 | * @type {string} 230 | * @memberof ApiPluginHaloRunV1alpha1LinkApiGetLinkDetail 231 | */ 232 | readonly url: string 233 | } 234 | 235 | /** 236 | * Request parameters for listLinks operation in ApiPluginHaloRunV1alpha1LinkApi. 237 | * @export 238 | * @interface ApiPluginHaloRunV1alpha1LinkApiListLinksRequest 239 | */ 240 | export interface ApiPluginHaloRunV1alpha1LinkApiListLinksRequest { 241 | /** 242 | * Keyword to search links under the group 243 | * @type {string} 244 | * @memberof ApiPluginHaloRunV1alpha1LinkApiListLinks 245 | */ 246 | readonly keyword?: string 247 | 248 | /** 249 | * Link group name 250 | * @type {string} 251 | * @memberof ApiPluginHaloRunV1alpha1LinkApiListLinks 252 | */ 253 | readonly groupName?: string 254 | 255 | /** 256 | * Page number. Default is 0. 257 | * @type {number} 258 | * @memberof ApiPluginHaloRunV1alpha1LinkApiListLinks 259 | */ 260 | readonly page?: number 261 | 262 | /** 263 | * Size number. Default is 0. 264 | * @type {number} 265 | * @memberof ApiPluginHaloRunV1alpha1LinkApiListLinks 266 | */ 267 | readonly size?: number 268 | 269 | /** 270 | * Label selector. e.g.: hidden!=true 271 | * @type {Array} 272 | * @memberof ApiPluginHaloRunV1alpha1LinkApiListLinks 273 | */ 274 | readonly labelSelector?: Array 275 | 276 | /** 277 | * Field selector. e.g.: metadata.name==halo 278 | * @type {Array} 279 | * @memberof ApiPluginHaloRunV1alpha1LinkApiListLinks 280 | */ 281 | readonly fieldSelector?: Array 282 | 283 | /** 284 | * Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported. 285 | * @type {Array} 286 | * @memberof ApiPluginHaloRunV1alpha1LinkApiListLinks 287 | */ 288 | readonly sort?: Array 289 | } 290 | 291 | /** 292 | * ApiPluginHaloRunV1alpha1LinkApi - object-oriented interface 293 | * @export 294 | * @class ApiPluginHaloRunV1alpha1LinkApi 295 | * @extends {BaseAPI} 296 | */ 297 | export class ApiPluginHaloRunV1alpha1LinkApi extends BaseAPI { 298 | /** 299 | * Get link detail by id 300 | * @param {ApiPluginHaloRunV1alpha1LinkApiGetLinkDetailRequest} requestParameters Request parameters. 301 | * @param {*} [options] Override http request option. 302 | * @throws {RequiredError} 303 | * @memberof ApiPluginHaloRunV1alpha1LinkApi 304 | */ 305 | public getLinkDetail(requestParameters: ApiPluginHaloRunV1alpha1LinkApiGetLinkDetailRequest, options?: RawAxiosRequestConfig) { 306 | return ApiPluginHaloRunV1alpha1LinkApiFp(this.configuration).getLinkDetail(requestParameters.url, options).then((request) => request(this.axios, this.basePath)); 307 | } 308 | 309 | /** 310 | * Lists link by query parameters 311 | * @param {ApiPluginHaloRunV1alpha1LinkApiListLinksRequest} requestParameters Request parameters. 312 | * @param {*} [options] Override http request option. 313 | * @throws {RequiredError} 314 | * @memberof ApiPluginHaloRunV1alpha1LinkApi 315 | */ 316 | public listLinks(requestParameters: ApiPluginHaloRunV1alpha1LinkApiListLinksRequest = {}, options?: RawAxiosRequestConfig) { 317 | return ApiPluginHaloRunV1alpha1LinkApiFp(this.configuration).listLinks(requestParameters.keyword, requestParameters.groupName, requestParameters.page, requestParameters.size, requestParameters.labelSelector, requestParameters.fieldSelector, requestParameters.sort, options).then((request) => request(this.axios, this.basePath)); 318 | } 319 | } 320 | 321 | -------------------------------------------------------------------------------- /console/src/views/LinkList.vue: -------------------------------------------------------------------------------- 1 | 305 | 488 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | charset = utf-8 3 | end_of_line = lf 4 | indent_size = 4 5 | indent_style = space 6 | insert_final_newline = false 7 | max_line_length = 120 8 | tab_width = 4 9 | ij_continuation_indent_size = 8 10 | ij_formatter_off_tag = @formatter:off 11 | ij_formatter_on_tag = @formatter:on 12 | ij_formatter_tags_enabled = false 13 | ij_smart_tabs = false 14 | ij_wrap_on_typing = false 15 | 16 | [*.java] 17 | max_line_length = 100 18 | ij_continuation_indent_size = 4 19 | ij_java_align_consecutive_assignments = false 20 | ij_java_align_consecutive_variable_declarations = false 21 | ij_java_align_group_field_declarations = false 22 | ij_java_align_multiline_annotation_parameters = false 23 | ij_java_align_multiline_array_initializer_expression = false 24 | ij_java_align_multiline_assignment = false 25 | ij_java_align_multiline_binary_operation = false 26 | ij_java_align_multiline_chained_methods = false 27 | ij_java_align_multiline_extends_list = false 28 | ij_java_align_multiline_for = true 29 | ij_java_align_multiline_method_parentheses = false 30 | ij_java_align_multiline_parameters = false 31 | ij_java_align_multiline_parameters_in_calls = false 32 | ij_java_align_multiline_parenthesized_expression = false 33 | ij_java_align_multiline_records = true 34 | ij_java_align_multiline_resources = true 35 | ij_java_align_multiline_ternary_operation = false 36 | ij_java_align_multiline_text_blocks = false 37 | ij_java_align_multiline_throws_list = false 38 | ij_java_align_subsequent_simple_methods = false 39 | ij_java_align_throws_keyword = false 40 | ij_java_annotation_parameter_wrap = off 41 | ij_java_array_initializer_new_line_after_left_brace = false 42 | ij_java_array_initializer_right_brace_on_new_line = false 43 | ij_java_array_initializer_wrap = normal 44 | ij_java_assert_statement_colon_on_next_line = false 45 | ij_java_assert_statement_wrap = normal 46 | ij_java_assignment_wrap = normal 47 | ij_java_binary_operation_sign_on_next_line = true 48 | ij_java_binary_operation_wrap = normal 49 | ij_java_blank_lines_after_anonymous_class_header = 0 50 | ij_java_blank_lines_after_class_header = 0 51 | ij_java_blank_lines_after_imports = 1 52 | ij_java_blank_lines_after_package = 1 53 | ij_java_blank_lines_around_class = 1 54 | ij_java_blank_lines_around_field = 0 55 | ij_java_blank_lines_around_field_in_interface = 0 56 | ij_java_blank_lines_around_initializer = 1 57 | ij_java_blank_lines_around_method = 1 58 | ij_java_blank_lines_around_method_in_interface = 1 59 | ij_java_blank_lines_before_class_end = 0 60 | ij_java_blank_lines_before_imports = 0 61 | ij_java_blank_lines_before_method_body = 0 62 | ij_java_blank_lines_before_package = 1 63 | ij_java_block_brace_style = end_of_line 64 | ij_java_block_comment_at_first_column = false 65 | ij_java_call_parameters_new_line_after_left_paren = false 66 | ij_java_call_parameters_right_paren_on_new_line = false 67 | ij_java_call_parameters_wrap = normal 68 | ij_java_case_statement_on_separate_line = true 69 | ij_java_catch_on_new_line = false 70 | ij_java_class_annotation_wrap = split_into_lines 71 | ij_java_class_brace_style = end_of_line 72 | ij_java_class_count_to_use_import_on_demand = 999 73 | ij_java_class_names_in_javadoc = 1 74 | ij_java_do_not_indent_top_level_class_members = false 75 | ij_java_do_not_wrap_after_single_annotation = false 76 | ij_java_do_while_brace_force = always 77 | ij_java_doc_add_blank_line_after_description = true 78 | ij_java_doc_add_blank_line_after_param_comments = false 79 | ij_java_doc_add_blank_line_after_return = false 80 | ij_java_doc_add_p_tag_on_empty_lines = true 81 | ij_java_doc_align_exception_comments = true 82 | ij_java_doc_align_param_comments = false 83 | ij_java_doc_do_not_wrap_if_one_line = false 84 | ij_java_doc_enable_formatting = true 85 | ij_java_doc_enable_leading_asterisks = true 86 | ij_java_doc_indent_on_continuation = false 87 | ij_java_doc_keep_empty_lines = true 88 | ij_java_doc_keep_empty_parameter_tag = true 89 | ij_java_doc_keep_empty_return_tag = true 90 | ij_java_doc_keep_empty_throws_tag = true 91 | ij_java_doc_keep_invalid_tags = true 92 | ij_java_doc_param_description_on_new_line = false 93 | ij_java_doc_preserve_line_breaks = false 94 | ij_java_doc_use_throws_not_exception_tag = true 95 | ij_java_else_on_new_line = false 96 | ij_java_enum_constants_wrap = normal 97 | ij_java_extends_keyword_wrap = normal 98 | ij_java_extends_list_wrap = normal 99 | ij_java_field_annotation_wrap = split_into_lines 100 | ij_java_finally_on_new_line = false 101 | ij_java_for_brace_force = always 102 | ij_java_for_statement_new_line_after_left_paren = false 103 | ij_java_for_statement_right_paren_on_new_line = false 104 | ij_java_for_statement_wrap = normal 105 | ij_java_generate_final_locals = false 106 | ij_java_generate_final_parameters = false 107 | ij_java_if_brace_force = always 108 | ij_java_imports_layout = $*, |, *, |, * 109 | ij_java_indent_case_from_switch = true 110 | ij_java_insert_inner_class_imports = false 111 | ij_java_insert_override_annotation = true 112 | ij_java_keep_blank_lines_before_right_brace = 2 113 | ij_java_keep_blank_lines_between_package_declaration_and_header = 2 114 | ij_java_keep_blank_lines_in_code = 2 115 | ij_java_keep_blank_lines_in_declarations = 2 116 | ij_java_keep_control_statement_in_one_line = true 117 | ij_java_keep_first_column_comment = true 118 | ij_java_keep_indents_on_empty_lines = false 119 | ij_java_keep_line_breaks = true 120 | ij_java_keep_multiple_expressions_in_one_line = false 121 | ij_java_keep_simple_blocks_in_one_line = false 122 | ij_java_keep_simple_classes_in_one_line = false 123 | ij_java_keep_simple_lambdas_in_one_line = false 124 | ij_java_keep_simple_methods_in_one_line = false 125 | ij_java_label_indent_absolute = false 126 | ij_java_label_indent_size = 0 127 | ij_java_lambda_brace_style = end_of_line 128 | ij_java_layout_static_imports_separately = true 129 | ij_java_line_comment_add_space = true 130 | ij_java_line_comment_at_first_column = false 131 | ij_java_method_annotation_wrap = split_into_lines 132 | ij_java_method_brace_style = end_of_line 133 | ij_java_method_call_chain_wrap = normal 134 | ij_java_method_parameters_new_line_after_left_paren = false 135 | ij_java_method_parameters_right_paren_on_new_line = false 136 | ij_java_method_parameters_wrap = normal 137 | ij_java_modifier_list_wrap = false 138 | ij_java_names_count_to_use_import_on_demand = 999 139 | ij_java_new_line_after_lparen_in_record_header = false 140 | ij_java_parameter_annotation_wrap = normal 141 | ij_java_parentheses_expression_new_line_after_left_paren = false 142 | ij_java_parentheses_expression_right_paren_on_new_line = false 143 | ij_java_place_assignment_sign_on_next_line = false 144 | ij_java_prefer_longer_names = true 145 | ij_java_prefer_parameters_wrap = false 146 | ij_java_record_components_wrap = normal 147 | ij_java_repeat_synchronized = true 148 | ij_java_replace_instanceof_and_cast = false 149 | ij_java_replace_null_check = true 150 | ij_java_replace_sum_lambda_with_method_ref = true 151 | ij_java_resource_list_new_line_after_left_paren = false 152 | ij_java_resource_list_right_paren_on_new_line = false 153 | ij_java_resource_list_wrap = normal 154 | ij_java_rparen_on_new_line_in_record_header = false 155 | ij_java_space_after_closing_angle_bracket_in_type_argument = false 156 | ij_java_space_after_colon = true 157 | ij_java_space_after_comma = true 158 | ij_java_space_after_comma_in_type_arguments = true 159 | ij_java_space_after_for_semicolon = true 160 | ij_java_space_after_quest = true 161 | ij_java_space_after_type_cast = true 162 | ij_java_space_before_annotation_array_initializer_left_brace = false 163 | ij_java_space_before_annotation_parameter_list = false 164 | ij_java_space_before_array_initializer_left_brace = true 165 | ij_java_space_before_catch_keyword = true 166 | ij_java_space_before_catch_left_brace = true 167 | ij_java_space_before_catch_parentheses = true 168 | ij_java_space_before_class_left_brace = true 169 | ij_java_space_before_colon = true 170 | ij_java_space_before_colon_in_foreach = true 171 | ij_java_space_before_comma = false 172 | ij_java_space_before_do_left_brace = true 173 | ij_java_space_before_else_keyword = true 174 | ij_java_space_before_else_left_brace = true 175 | ij_java_space_before_finally_keyword = true 176 | ij_java_space_before_finally_left_brace = true 177 | ij_java_space_before_for_left_brace = true 178 | ij_java_space_before_for_parentheses = true 179 | ij_java_space_before_for_semicolon = false 180 | ij_java_space_before_if_left_brace = true 181 | ij_java_space_before_if_parentheses = true 182 | ij_java_space_before_method_call_parentheses = false 183 | ij_java_space_before_method_left_brace = true 184 | ij_java_space_before_method_parentheses = false 185 | ij_java_space_before_opening_angle_bracket_in_type_parameter = false 186 | ij_java_space_before_quest = true 187 | ij_java_space_before_switch_left_brace = true 188 | ij_java_space_before_switch_parentheses = true 189 | ij_java_space_before_synchronized_left_brace = true 190 | ij_java_space_before_synchronized_parentheses = true 191 | ij_java_space_before_try_left_brace = true 192 | ij_java_space_before_try_parentheses = true 193 | ij_java_space_before_type_parameter_list = false 194 | ij_java_space_before_while_keyword = true 195 | ij_java_space_before_while_left_brace = true 196 | ij_java_space_before_while_parentheses = true 197 | ij_java_space_inside_one_line_enum_braces = false 198 | ij_java_space_within_empty_array_initializer_braces = false 199 | ij_java_space_within_empty_method_call_parentheses = false 200 | ij_java_space_within_empty_method_parentheses = false 201 | ij_java_spaces_around_additive_operators = true 202 | ij_java_spaces_around_assignment_operators = true 203 | ij_java_spaces_around_bitwise_operators = true 204 | ij_java_spaces_around_equality_operators = true 205 | ij_java_spaces_around_lambda_arrow = true 206 | ij_java_spaces_around_logical_operators = true 207 | ij_java_spaces_around_method_ref_dbl_colon = false 208 | ij_java_spaces_around_multiplicative_operators = true 209 | ij_java_spaces_around_relational_operators = true 210 | ij_java_spaces_around_shift_operators = true 211 | ij_java_spaces_around_type_bounds_in_type_parameters = true 212 | ij_java_spaces_around_unary_operator = false 213 | ij_java_spaces_within_angle_brackets = false 214 | ij_java_spaces_within_annotation_parentheses = false 215 | ij_java_spaces_within_array_initializer_braces = false 216 | ij_java_spaces_within_braces = false 217 | ij_java_spaces_within_brackets = false 218 | ij_java_spaces_within_cast_parentheses = false 219 | ij_java_spaces_within_catch_parentheses = false 220 | ij_java_spaces_within_for_parentheses = false 221 | ij_java_spaces_within_if_parentheses = false 222 | ij_java_spaces_within_method_call_parentheses = false 223 | ij_java_spaces_within_method_parentheses = false 224 | ij_java_spaces_within_parentheses = false 225 | ij_java_spaces_within_switch_parentheses = false 226 | ij_java_spaces_within_synchronized_parentheses = false 227 | ij_java_spaces_within_try_parentheses = false 228 | ij_java_spaces_within_while_parentheses = false 229 | ij_java_special_else_if_treatment = true 230 | ij_java_subclass_name_suffix = Impl 231 | ij_java_ternary_operation_signs_on_next_line = true 232 | ij_java_ternary_operation_wrap = normal 233 | ij_java_test_name_suffix = Test 234 | ij_java_throws_keyword_wrap = normal 235 | ij_java_throws_list_wrap = normal 236 | ij_java_use_external_annotations = false 237 | ij_java_use_fq_class_names = false 238 | ij_java_use_relative_indents = false 239 | ij_java_use_single_class_imports = true 240 | ij_java_variable_annotation_wrap = normal 241 | ij_java_visibility = public 242 | ij_java_while_brace_force = always 243 | ij_java_while_on_new_line = false 244 | ij_java_wrap_comments = false 245 | ij_java_wrap_first_method_in_call_chain = false 246 | ij_java_wrap_long_lines = true 247 | 248 | [*.properties] 249 | ij_properties_align_group_field_declarations = false 250 | ij_properties_keep_blank_lines = false 251 | ij_properties_key_value_delimiter = equals 252 | ij_properties_spaces_around_key_value_delimiter = false 253 | 254 | [.editorconfig] 255 | ij_editorconfig_align_group_field_declarations = false 256 | ij_editorconfig_space_after_colon = false 257 | ij_editorconfig_space_after_comma = true 258 | ij_editorconfig_space_before_colon = false 259 | ij_editorconfig_space_before_comma = false 260 | ij_editorconfig_spaces_around_assignment_operators = true 261 | 262 | [{*.ant, *.fxml, *.jhm, *.jnlp, *.jrxml, *.jspx, *.pom, *.rng, *.tagx, *.tld, *.wsdl, *.xml, *.xsd, *.xsl, *.xslt, *.xul}] 263 | ij_xml_align_attributes = true 264 | ij_xml_align_text = false 265 | ij_xml_attribute_wrap = normal 266 | ij_xml_block_comment_at_first_column = true 267 | ij_xml_keep_blank_lines = 2 268 | ij_xml_keep_indents_on_empty_lines = false 269 | ij_xml_keep_line_breaks = true 270 | ij_xml_keep_line_breaks_in_text = true 271 | ij_xml_keep_whitespaces = false 272 | ij_xml_keep_whitespaces_around_cdata = preserve 273 | ij_xml_keep_whitespaces_inside_cdata = false 274 | ij_xml_line_comment_at_first_column = true 275 | ij_xml_space_after_tag_name = false 276 | ij_xml_space_around_equals_in_attribute = false 277 | ij_xml_space_inside_empty_tag = false 278 | ij_xml_text_wrap = normal 279 | 280 | [{*.bash, *.sh, *.zsh}] 281 | indent_size = 2 282 | tab_width = 2 283 | ij_shell_binary_ops_start_line = false 284 | ij_shell_keep_column_alignment_padding = false 285 | ij_shell_minify_program = false 286 | ij_shell_redirect_followed_by_space = false 287 | ij_shell_switch_cases_indented = false 288 | 289 | [{*.gant, *.gradle, *.groovy, *.gy}] 290 | ij_groovy_align_group_field_declarations = false 291 | ij_groovy_align_multiline_array_initializer_expression = false 292 | ij_groovy_align_multiline_assignment = false 293 | ij_groovy_align_multiline_binary_operation = false 294 | ij_groovy_align_multiline_chained_methods = false 295 | ij_groovy_align_multiline_extends_list = false 296 | ij_groovy_align_multiline_for = true 297 | ij_groovy_align_multiline_list_or_map = true 298 | ij_groovy_align_multiline_method_parentheses = false 299 | ij_groovy_align_multiline_parameters = true 300 | ij_groovy_align_multiline_parameters_in_calls = false 301 | ij_groovy_align_multiline_resources = true 302 | ij_groovy_align_multiline_ternary_operation = false 303 | ij_groovy_align_multiline_throws_list = false 304 | ij_groovy_align_named_args_in_map = true 305 | ij_groovy_align_throws_keyword = false 306 | ij_groovy_array_initializer_new_line_after_left_brace = false 307 | ij_groovy_array_initializer_right_brace_on_new_line = false 308 | ij_groovy_array_initializer_wrap = off 309 | ij_groovy_assert_statement_wrap = off 310 | ij_groovy_assignment_wrap = off 311 | ij_groovy_binary_operation_wrap = off 312 | ij_groovy_blank_lines_after_class_header = 0 313 | ij_groovy_blank_lines_after_imports = 1 314 | ij_groovy_blank_lines_after_package = 1 315 | ij_groovy_blank_lines_around_class = 1 316 | ij_groovy_blank_lines_around_field = 0 317 | ij_groovy_blank_lines_around_field_in_interface = 0 318 | ij_groovy_blank_lines_around_method = 1 319 | ij_groovy_blank_lines_around_method_in_interface = 1 320 | ij_groovy_blank_lines_before_imports = 1 321 | ij_groovy_blank_lines_before_method_body = 0 322 | ij_groovy_blank_lines_before_package = 0 323 | ij_groovy_block_brace_style = end_of_line 324 | ij_groovy_block_comment_at_first_column = true 325 | ij_groovy_call_parameters_new_line_after_left_paren = false 326 | ij_groovy_call_parameters_right_paren_on_new_line = false 327 | ij_groovy_call_parameters_wrap = off 328 | ij_groovy_catch_on_new_line = false 329 | ij_groovy_class_annotation_wrap = split_into_lines 330 | ij_groovy_class_brace_style = end_of_line 331 | ij_groovy_class_count_to_use_import_on_demand = 5 332 | ij_groovy_do_while_brace_force = never 333 | ij_groovy_else_on_new_line = false 334 | ij_groovy_enum_constants_wrap = off 335 | ij_groovy_extends_keyword_wrap = off 336 | ij_groovy_extends_list_wrap = off 337 | ij_groovy_field_annotation_wrap = split_into_lines 338 | ij_groovy_finally_on_new_line = false 339 | ij_groovy_for_brace_force = never 340 | ij_groovy_for_statement_new_line_after_left_paren = false 341 | ij_groovy_for_statement_right_paren_on_new_line = false 342 | ij_groovy_for_statement_wrap = off 343 | ij_groovy_if_brace_force = never 344 | ij_groovy_import_annotation_wrap = 2 345 | ij_groovy_indent_case_from_switch = true 346 | ij_groovy_indent_label_blocks = true 347 | ij_groovy_insert_inner_class_imports = false 348 | ij_groovy_keep_blank_lines_before_right_brace = 2 349 | ij_groovy_keep_blank_lines_in_code = 2 350 | ij_groovy_keep_blank_lines_in_declarations = 2 351 | ij_groovy_keep_control_statement_in_one_line = true 352 | ij_groovy_keep_first_column_comment = true 353 | ij_groovy_keep_indents_on_empty_lines = false 354 | ij_groovy_keep_line_breaks = true 355 | ij_groovy_keep_multiple_expressions_in_one_line = false 356 | ij_groovy_keep_simple_blocks_in_one_line = false 357 | ij_groovy_keep_simple_classes_in_one_line = true 358 | ij_groovy_keep_simple_lambdas_in_one_line = true 359 | ij_groovy_keep_simple_methods_in_one_line = true 360 | ij_groovy_label_indent_absolute = false 361 | ij_groovy_label_indent_size = 0 362 | ij_groovy_lambda_brace_style = end_of_line 363 | ij_groovy_layout_static_imports_separately = true 364 | ij_groovy_line_comment_add_space = false 365 | ij_groovy_line_comment_at_first_column = true 366 | ij_groovy_method_annotation_wrap = split_into_lines 367 | ij_groovy_method_brace_style = end_of_line 368 | ij_groovy_method_call_chain_wrap = off 369 | ij_groovy_method_parameters_new_line_after_left_paren = false 370 | ij_groovy_method_parameters_right_paren_on_new_line = false 371 | ij_groovy_method_parameters_wrap = off 372 | ij_groovy_modifier_list_wrap = false 373 | ij_groovy_names_count_to_use_import_on_demand = 3 374 | ij_groovy_parameter_annotation_wrap = off 375 | ij_groovy_parentheses_expression_new_line_after_left_paren = false 376 | ij_groovy_parentheses_expression_right_paren_on_new_line = false 377 | ij_groovy_prefer_parameters_wrap = false 378 | ij_groovy_resource_list_new_line_after_left_paren = false 379 | ij_groovy_resource_list_right_paren_on_new_line = false 380 | ij_groovy_resource_list_wrap = off 381 | ij_groovy_space_after_assert_separator = true 382 | ij_groovy_space_after_colon = true 383 | ij_groovy_space_after_comma = true 384 | ij_groovy_space_after_comma_in_type_arguments = true 385 | ij_groovy_space_after_for_semicolon = true 386 | ij_groovy_space_after_quest = true 387 | ij_groovy_space_after_type_cast = true 388 | ij_groovy_space_before_annotation_parameter_list = false 389 | ij_groovy_space_before_array_initializer_left_brace = false 390 | ij_groovy_space_before_assert_separator = false 391 | ij_groovy_space_before_catch_keyword = true 392 | ij_groovy_space_before_catch_left_brace = true 393 | ij_groovy_space_before_catch_parentheses = true 394 | ij_groovy_space_before_class_left_brace = true 395 | ij_groovy_space_before_closure_left_brace = true 396 | ij_groovy_space_before_colon = true 397 | ij_groovy_space_before_comma = false 398 | ij_groovy_space_before_do_left_brace = true 399 | ij_groovy_space_before_else_keyword = true 400 | ij_groovy_space_before_else_left_brace = true 401 | ij_groovy_space_before_finally_keyword = true 402 | ij_groovy_space_before_finally_left_brace = true 403 | ij_groovy_space_before_for_left_brace = true 404 | ij_groovy_space_before_for_parentheses = true 405 | ij_groovy_space_before_for_semicolon = false 406 | ij_groovy_space_before_if_left_brace = true 407 | ij_groovy_space_before_if_parentheses = true 408 | ij_groovy_space_before_method_call_parentheses = false 409 | ij_groovy_space_before_method_left_brace = true 410 | ij_groovy_space_before_method_parentheses = false 411 | ij_groovy_space_before_quest = true 412 | ij_groovy_space_before_switch_left_brace = true 413 | ij_groovy_space_before_switch_parentheses = true 414 | ij_groovy_space_before_synchronized_left_brace = true 415 | ij_groovy_space_before_synchronized_parentheses = true 416 | ij_groovy_space_before_try_left_brace = true 417 | ij_groovy_space_before_try_parentheses = true 418 | ij_groovy_space_before_while_keyword = true 419 | ij_groovy_space_before_while_left_brace = true 420 | ij_groovy_space_before_while_parentheses = true 421 | ij_groovy_space_in_named_argument = true 422 | ij_groovy_space_in_named_argument_before_colon = false 423 | ij_groovy_space_within_empty_array_initializer_braces = false 424 | ij_groovy_space_within_empty_method_call_parentheses = false 425 | ij_groovy_spaces_around_additive_operators = true 426 | ij_groovy_spaces_around_assignment_operators = true 427 | ij_groovy_spaces_around_bitwise_operators = true 428 | ij_groovy_spaces_around_equality_operators = true 429 | ij_groovy_spaces_around_lambda_arrow = true 430 | ij_groovy_spaces_around_logical_operators = true 431 | ij_groovy_spaces_around_multiplicative_operators = true 432 | ij_groovy_spaces_around_regex_operators = true 433 | ij_groovy_spaces_around_relational_operators = true 434 | ij_groovy_spaces_around_shift_operators = true 435 | ij_groovy_spaces_within_annotation_parentheses = false 436 | ij_groovy_spaces_within_array_initializer_braces = false 437 | ij_groovy_spaces_within_braces = true 438 | ij_groovy_spaces_within_brackets = false 439 | ij_groovy_spaces_within_cast_parentheses = false 440 | ij_groovy_spaces_within_catch_parentheses = false 441 | ij_groovy_spaces_within_for_parentheses = false 442 | ij_groovy_spaces_within_gstring_injection_braces = false 443 | ij_groovy_spaces_within_if_parentheses = false 444 | ij_groovy_spaces_within_list_or_map = false 445 | ij_groovy_spaces_within_method_call_parentheses = false 446 | ij_groovy_spaces_within_method_parentheses = false 447 | ij_groovy_spaces_within_parentheses = false 448 | ij_groovy_spaces_within_switch_parentheses = false 449 | ij_groovy_spaces_within_synchronized_parentheses = false 450 | ij_groovy_spaces_within_try_parentheses = false 451 | ij_groovy_spaces_within_tuple_expression = false 452 | ij_groovy_spaces_within_while_parentheses = false 453 | ij_groovy_special_else_if_treatment = true 454 | ij_groovy_ternary_operation_wrap = off 455 | ij_groovy_throws_keyword_wrap = off 456 | ij_groovy_throws_list_wrap = off 457 | ij_groovy_use_flying_geese_braces = false 458 | ij_groovy_use_fq_class_names = false 459 | ij_groovy_use_fq_class_names_in_javadoc = true 460 | ij_groovy_use_relative_indents = false 461 | ij_groovy_use_single_class_imports = true 462 | ij_groovy_variable_annotation_wrap = off 463 | ij_groovy_while_brace_force = never 464 | ij_groovy_while_on_new_line = false 465 | ij_groovy_wrap_long_lines = false 466 | 467 | [{*.har, *.json}] 468 | indent_size = 2 469 | ij_json_keep_blank_lines_in_code = 0 470 | ij_json_keep_indents_on_empty_lines = false 471 | ij_json_keep_line_breaks = true 472 | ij_json_space_after_colon = true 473 | ij_json_space_after_comma = true 474 | ij_json_space_before_colon = true 475 | ij_json_space_before_comma = false 476 | ij_json_spaces_within_braces = false 477 | ij_json_spaces_within_brackets = false 478 | ij_json_wrap_long_lines = false 479 | 480 | [{*.htm, *.html, *.sht, *.shtm, *.shtml}] 481 | ij_html_add_new_line_before_tags = body, div, p, form, h1, h2, h3 482 | ij_html_align_attributes = true 483 | ij_html_align_text = false 484 | ij_html_attribute_wrap = normal 485 | ij_html_block_comment_at_first_column = true 486 | ij_html_do_not_align_children_of_min_lines = 0 487 | ij_html_do_not_break_if_inline_tags = title, h1, h2, h3, h4, h5, h6, p 488 | ij_html_do_not_indent_children_of_tags = html, body, thead, tbody, tfoot 489 | ij_html_enforce_quotes = false 490 | ij_html_inline_tags = a, abbr, acronym, b, basefont, bdo, big, br, cite, cite, code, dfn, em, font, i, img, input, kbd, label, q, s, samp, select, small, span, strike, strong, sub, sup, textarea, tt, u, var 491 | ij_html_keep_blank_lines = 2 492 | ij_html_keep_indents_on_empty_lines = false 493 | ij_html_keep_line_breaks = true 494 | ij_html_keep_line_breaks_in_text = true 495 | ij_html_keep_whitespaces = false 496 | ij_html_keep_whitespaces_inside = span, pre, textarea 497 | ij_html_line_comment_at_first_column = true 498 | ij_html_new_line_after_last_attribute = never 499 | ij_html_new_line_before_first_attribute = never 500 | ij_html_quote_style = double 501 | ij_html_remove_new_line_before_tags = br 502 | ij_html_space_after_tag_name = false 503 | ij_html_space_around_equality_in_attribute = false 504 | ij_html_space_inside_empty_tag = false 505 | ij_html_text_wrap = normal 506 | 507 | [{*.yaml, *.yml}] 508 | indent_size = 2 509 | ij_yaml_keep_indents_on_empty_lines = false 510 | ij_yaml_keep_line_breaks = true 511 | -------------------------------------------------------------------------------- /api-docs/openapi/v3_0/linksV1alpha1Api.json: -------------------------------------------------------------------------------- 1 | { 2 | "openapi" : "3.0.1", 3 | "info" : { 4 | "title" : "Halo", 5 | "version" : "2.17.2" 6 | }, 7 | "servers" : [ { 8 | "url" : "http://localhost:31788", 9 | "description" : "Generated server url" 10 | } ], 11 | "security" : [ { 12 | "basicAuth" : [ ], 13 | "bearerAuth" : [ ] 14 | } ], 15 | "paths" : { 16 | "/apis/api.plugin.halo.run/v1alpha1/plugins/PluginLinks/link-detail" : { 17 | "get" : { 18 | "description" : "Get link detail by id", 19 | "operationId" : "GetLinkDetail", 20 | "parameters" : [ { 21 | "description" : "Link url", 22 | "in" : "query", 23 | "name" : "url", 24 | "required" : true, 25 | "schema" : { 26 | "type" : "string" 27 | } 28 | } ], 29 | "responses" : { 30 | "default" : { 31 | "content" : { 32 | "*/*" : { 33 | "schema" : { 34 | "$ref" : "#/components/schemas/LinkDetailDTO" 35 | } 36 | } 37 | }, 38 | "description" : "default response" 39 | } 40 | }, 41 | "tags" : [ "api.plugin.halo.run/v1alpha1/Link" ] 42 | } 43 | }, 44 | "/apis/api.plugin.halo.run/v1alpha1/plugins/PluginLinks/links" : { 45 | "get" : { 46 | "description" : "Lists link by query parameters", 47 | "operationId" : "listLinks", 48 | "parameters" : [ { 49 | "description" : "Keyword to search links under the group", 50 | "in" : "query", 51 | "name" : "keyword", 52 | "schema" : { 53 | "type" : "string" 54 | } 55 | }, { 56 | "description" : "Link group name", 57 | "in" : "query", 58 | "name" : "groupName", 59 | "schema" : { 60 | "type" : "string" 61 | } 62 | }, { 63 | "description" : "Page number. Default is 0.", 64 | "in" : "query", 65 | "name" : "page", 66 | "schema" : { 67 | "type" : "integer", 68 | "format" : "int32" 69 | } 70 | }, { 71 | "description" : "Size number. Default is 0.", 72 | "in" : "query", 73 | "name" : "size", 74 | "schema" : { 75 | "type" : "integer", 76 | "format" : "int32" 77 | } 78 | }, { 79 | "description" : "Label selector. e.g.: hidden!=true", 80 | "in" : "query", 81 | "name" : "labelSelector", 82 | "schema" : { 83 | "type" : "array", 84 | "items" : { 85 | "type" : "string" 86 | } 87 | } 88 | }, { 89 | "description" : "Field selector. e.g.: metadata.name==halo", 90 | "in" : "query", 91 | "name" : "fieldSelector", 92 | "schema" : { 93 | "type" : "array", 94 | "items" : { 95 | "type" : "string" 96 | } 97 | } 98 | }, { 99 | "description" : "Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported.", 100 | "in" : "query", 101 | "name" : "sort", 102 | "schema" : { 103 | "type" : "array", 104 | "items" : { 105 | "type" : "string" 106 | } 107 | } 108 | } ], 109 | "responses" : { 110 | "default" : { 111 | "content" : { 112 | "*/*" : { 113 | "schema" : { 114 | "$ref" : "#/components/schemas/LinkList" 115 | } 116 | } 117 | }, 118 | "description" : "default response" 119 | } 120 | }, 121 | "tags" : [ "api.plugin.halo.run/v1alpha1/Link" ] 122 | } 123 | }, 124 | "/apis/core.halo.run/v1alpha1/linkgroups" : { 125 | "get" : { 126 | "description" : "List LinkGroup", 127 | "operationId" : "listLinkGroup", 128 | "parameters" : [ { 129 | "description" : "Page number. Default is 0.", 130 | "in" : "query", 131 | "name" : "page", 132 | "schema" : { 133 | "type" : "integer", 134 | "format" : "int32" 135 | } 136 | }, { 137 | "description" : "Size number. Default is 0.", 138 | "in" : "query", 139 | "name" : "size", 140 | "schema" : { 141 | "type" : "integer", 142 | "format" : "int32" 143 | } 144 | }, { 145 | "description" : "Label selector. e.g.: hidden!=true", 146 | "in" : "query", 147 | "name" : "labelSelector", 148 | "schema" : { 149 | "type" : "array", 150 | "items" : { 151 | "type" : "string" 152 | } 153 | } 154 | }, { 155 | "description" : "Field selector. e.g.: metadata.name==halo", 156 | "in" : "query", 157 | "name" : "fieldSelector", 158 | "schema" : { 159 | "type" : "array", 160 | "items" : { 161 | "type" : "string" 162 | } 163 | } 164 | }, { 165 | "description" : "Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported.", 166 | "in" : "query", 167 | "name" : "sort", 168 | "schema" : { 169 | "type" : "array", 170 | "items" : { 171 | "type" : "string" 172 | } 173 | } 174 | } ], 175 | "responses" : { 176 | "200" : { 177 | "content" : { 178 | "*/*" : { 179 | "schema" : { 180 | "$ref" : "#/components/schemas/LinkGroupList" 181 | } 182 | } 183 | }, 184 | "description" : "Response linkgroups" 185 | } 186 | }, 187 | "tags" : [ "LinkGroupV1alpha1" ] 188 | }, 189 | "post" : { 190 | "description" : "Create LinkGroup", 191 | "operationId" : "createLinkGroup", 192 | "requestBody" : { 193 | "content" : { 194 | "*/*" : { 195 | "schema" : { 196 | "$ref" : "#/components/schemas/LinkGroup" 197 | } 198 | } 199 | }, 200 | "description" : "Fresh linkgroup" 201 | }, 202 | "responses" : { 203 | "200" : { 204 | "content" : { 205 | "*/*" : { 206 | "schema" : { 207 | "$ref" : "#/components/schemas/LinkGroup" 208 | } 209 | } 210 | }, 211 | "description" : "Response linkgroups created just now" 212 | } 213 | }, 214 | "tags" : [ "LinkGroupV1alpha1" ] 215 | } 216 | }, 217 | "/apis/core.halo.run/v1alpha1/linkgroups/{name}" : { 218 | "delete" : { 219 | "description" : "Delete LinkGroup", 220 | "operationId" : "deleteLinkGroup", 221 | "parameters" : [ { 222 | "description" : "Name of linkgroup", 223 | "in" : "path", 224 | "name" : "name", 225 | "required" : true, 226 | "schema" : { 227 | "type" : "string" 228 | } 229 | } ], 230 | "responses" : { 231 | "200" : { 232 | "description" : "Response linkgroup deleted just now" 233 | } 234 | }, 235 | "tags" : [ "LinkGroupV1alpha1" ] 236 | }, 237 | "get" : { 238 | "description" : "Get LinkGroup", 239 | "operationId" : "getLinkGroup", 240 | "parameters" : [ { 241 | "description" : "Name of linkgroup", 242 | "in" : "path", 243 | "name" : "name", 244 | "required" : true, 245 | "schema" : { 246 | "type" : "string" 247 | } 248 | } ], 249 | "responses" : { 250 | "200" : { 251 | "content" : { 252 | "*/*" : { 253 | "schema" : { 254 | "$ref" : "#/components/schemas/LinkGroup" 255 | } 256 | } 257 | }, 258 | "description" : "Response single linkgroup" 259 | } 260 | }, 261 | "tags" : [ "LinkGroupV1alpha1" ] 262 | }, 263 | "patch" : { 264 | "description" : "Patch LinkGroup", 265 | "operationId" : "patchLinkGroup", 266 | "parameters" : [ { 267 | "description" : "Name of linkgroup", 268 | "in" : "path", 269 | "name" : "name", 270 | "required" : true, 271 | "schema" : { 272 | "type" : "string" 273 | } 274 | } ], 275 | "requestBody" : { 276 | "content" : { 277 | "application/json-patch+json" : { 278 | "schema" : { 279 | "$ref" : "#/components/schemas/JsonPatch" 280 | } 281 | } 282 | } 283 | }, 284 | "responses" : { 285 | "200" : { 286 | "content" : { 287 | "*/*" : { 288 | "schema" : { 289 | "$ref" : "#/components/schemas/LinkGroup" 290 | } 291 | } 292 | }, 293 | "description" : "Response linkgroup patched just now" 294 | } 295 | }, 296 | "tags" : [ "LinkGroupV1alpha1" ] 297 | }, 298 | "put" : { 299 | "description" : "Update LinkGroup", 300 | "operationId" : "updateLinkGroup", 301 | "parameters" : [ { 302 | "description" : "Name of linkgroup", 303 | "in" : "path", 304 | "name" : "name", 305 | "required" : true, 306 | "schema" : { 307 | "type" : "string" 308 | } 309 | } ], 310 | "requestBody" : { 311 | "content" : { 312 | "*/*" : { 313 | "schema" : { 314 | "$ref" : "#/components/schemas/LinkGroup" 315 | } 316 | } 317 | }, 318 | "description" : "Updated linkgroup" 319 | }, 320 | "responses" : { 321 | "200" : { 322 | "content" : { 323 | "*/*" : { 324 | "schema" : { 325 | "$ref" : "#/components/schemas/LinkGroup" 326 | } 327 | } 328 | }, 329 | "description" : "Response linkgroups updated just now" 330 | } 331 | }, 332 | "tags" : [ "LinkGroupV1alpha1" ] 333 | } 334 | }, 335 | "/apis/core.halo.run/v1alpha1/links" : { 336 | "get" : { 337 | "description" : "List Link", 338 | "operationId" : "listLink", 339 | "parameters" : [ { 340 | "description" : "Page number. Default is 0.", 341 | "in" : "query", 342 | "name" : "page", 343 | "schema" : { 344 | "type" : "integer", 345 | "format" : "int32" 346 | } 347 | }, { 348 | "description" : "Size number. Default is 0.", 349 | "in" : "query", 350 | "name" : "size", 351 | "schema" : { 352 | "type" : "integer", 353 | "format" : "int32" 354 | } 355 | }, { 356 | "description" : "Label selector. e.g.: hidden!=true", 357 | "in" : "query", 358 | "name" : "labelSelector", 359 | "schema" : { 360 | "type" : "array", 361 | "items" : { 362 | "type" : "string" 363 | } 364 | } 365 | }, { 366 | "description" : "Field selector. e.g.: metadata.name==halo", 367 | "in" : "query", 368 | "name" : "fieldSelector", 369 | "schema" : { 370 | "type" : "array", 371 | "items" : { 372 | "type" : "string" 373 | } 374 | } 375 | }, { 376 | "description" : "Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported.", 377 | "in" : "query", 378 | "name" : "sort", 379 | "schema" : { 380 | "type" : "array", 381 | "items" : { 382 | "type" : "string" 383 | } 384 | } 385 | } ], 386 | "responses" : { 387 | "200" : { 388 | "content" : { 389 | "*/*" : { 390 | "schema" : { 391 | "$ref" : "#/components/schemas/LinkList" 392 | } 393 | } 394 | }, 395 | "description" : "Response links" 396 | } 397 | }, 398 | "tags" : [ "LinkV1alpha1" ] 399 | }, 400 | "post" : { 401 | "description" : "Create Link", 402 | "operationId" : "createLink", 403 | "requestBody" : { 404 | "content" : { 405 | "*/*" : { 406 | "schema" : { 407 | "$ref" : "#/components/schemas/Link" 408 | } 409 | } 410 | }, 411 | "description" : "Fresh link" 412 | }, 413 | "responses" : { 414 | "200" : { 415 | "content" : { 416 | "*/*" : { 417 | "schema" : { 418 | "$ref" : "#/components/schemas/Link" 419 | } 420 | } 421 | }, 422 | "description" : "Response links created just now" 423 | } 424 | }, 425 | "tags" : [ "LinkV1alpha1" ] 426 | } 427 | }, 428 | "/apis/core.halo.run/v1alpha1/links/{name}" : { 429 | "delete" : { 430 | "description" : "Delete Link", 431 | "operationId" : "deleteLink", 432 | "parameters" : [ { 433 | "description" : "Name of link", 434 | "in" : "path", 435 | "name" : "name", 436 | "required" : true, 437 | "schema" : { 438 | "type" : "string" 439 | } 440 | } ], 441 | "responses" : { 442 | "200" : { 443 | "description" : "Response link deleted just now" 444 | } 445 | }, 446 | "tags" : [ "LinkV1alpha1" ] 447 | }, 448 | "get" : { 449 | "description" : "Get Link", 450 | "operationId" : "getLink", 451 | "parameters" : [ { 452 | "description" : "Name of link", 453 | "in" : "path", 454 | "name" : "name", 455 | "required" : true, 456 | "schema" : { 457 | "type" : "string" 458 | } 459 | } ], 460 | "responses" : { 461 | "200" : { 462 | "content" : { 463 | "*/*" : { 464 | "schema" : { 465 | "$ref" : "#/components/schemas/Link" 466 | } 467 | } 468 | }, 469 | "description" : "Response single link" 470 | } 471 | }, 472 | "tags" : [ "LinkV1alpha1" ] 473 | }, 474 | "patch" : { 475 | "description" : "Patch Link", 476 | "operationId" : "patchLink", 477 | "parameters" : [ { 478 | "description" : "Name of link", 479 | "in" : "path", 480 | "name" : "name", 481 | "required" : true, 482 | "schema" : { 483 | "type" : "string" 484 | } 485 | } ], 486 | "requestBody" : { 487 | "content" : { 488 | "application/json-patch+json" : { 489 | "schema" : { 490 | "$ref" : "#/components/schemas/JsonPatch" 491 | } 492 | } 493 | } 494 | }, 495 | "responses" : { 496 | "200" : { 497 | "content" : { 498 | "*/*" : { 499 | "schema" : { 500 | "$ref" : "#/components/schemas/Link" 501 | } 502 | } 503 | }, 504 | "description" : "Response link patched just now" 505 | } 506 | }, 507 | "tags" : [ "LinkV1alpha1" ] 508 | }, 509 | "put" : { 510 | "description" : "Update Link", 511 | "operationId" : "updateLink", 512 | "parameters" : [ { 513 | "description" : "Name of link", 514 | "in" : "path", 515 | "name" : "name", 516 | "required" : true, 517 | "schema" : { 518 | "type" : "string" 519 | } 520 | } ], 521 | "requestBody" : { 522 | "content" : { 523 | "*/*" : { 524 | "schema" : { 525 | "$ref" : "#/components/schemas/Link" 526 | } 527 | } 528 | }, 529 | "description" : "Updated link" 530 | }, 531 | "responses" : { 532 | "200" : { 533 | "content" : { 534 | "*/*" : { 535 | "schema" : { 536 | "$ref" : "#/components/schemas/Link" 537 | } 538 | } 539 | }, 540 | "description" : "Response links updated just now" 541 | } 542 | }, 543 | "tags" : [ "LinkV1alpha1" ] 544 | } 545 | } 546 | }, 547 | "components" : { 548 | "schemas" : { 549 | "AddOperation" : { 550 | "required" : [ "op", "path", "value" ], 551 | "type" : "object", 552 | "properties" : { 553 | "op" : { 554 | "type" : "string", 555 | "enum" : [ "add" ] 556 | }, 557 | "path" : { 558 | "pattern" : "^(/[^/~]*(~[01][^/~]*)*)*$", 559 | "type" : "string", 560 | "description" : "A JSON Pointer path pointing to the location to move/copy from.", 561 | "example" : "/a/b/c" 562 | }, 563 | "value" : { 564 | "description" : "Value can be any JSON value" 565 | } 566 | } 567 | }, 568 | "CopyOperation" : { 569 | "required" : [ "op", "from", "path" ], 570 | "type" : "object", 571 | "properties" : { 572 | "from" : { 573 | "pattern" : "^(/[^/~]*(~[01][^/~]*)*)*$", 574 | "type" : "string", 575 | "description" : "A JSON Pointer path pointing to the location to move/copy from.", 576 | "example" : "/a/b/c" 577 | }, 578 | "op" : { 579 | "type" : "string", 580 | "enum" : [ "copy" ] 581 | }, 582 | "path" : { 583 | "pattern" : "^(/[^/~]*(~[01][^/~]*)*)*$", 584 | "type" : "string", 585 | "description" : "A JSON Pointer path pointing to the location to move/copy from.", 586 | "example" : "/a/b/c" 587 | } 588 | } 589 | }, 590 | "JsonPatch" : { 591 | "minItems" : 1, 592 | "uniqueItems" : true, 593 | "type" : "array", 594 | "description" : "JSON schema for JSONPatch operations", 595 | "items" : { 596 | "oneOf" : [ { 597 | "$ref" : "#/components/schemas/AddOperation" 598 | }, { 599 | "$ref" : "#/components/schemas/ReplaceOperation" 600 | }, { 601 | "$ref" : "#/components/schemas/TestOperation" 602 | }, { 603 | "$ref" : "#/components/schemas/RemoveOperation" 604 | }, { 605 | "$ref" : "#/components/schemas/MoveOperation" 606 | }, { 607 | "$ref" : "#/components/schemas/CopyOperation" 608 | } ] 609 | } 610 | }, 611 | "Link" : { 612 | "required" : [ "apiVersion", "kind", "metadata" ], 613 | "type" : "object", 614 | "properties" : { 615 | "apiVersion" : { 616 | "type" : "string" 617 | }, 618 | "kind" : { 619 | "type" : "string" 620 | }, 621 | "metadata" : { 622 | "$ref" : "#/components/schemas/Metadata" 623 | }, 624 | "spec" : { 625 | "$ref" : "#/components/schemas/LinkSpec" 626 | } 627 | } 628 | }, 629 | "LinkDetailDTO" : { 630 | "required" : [ "title" ], 631 | "type" : "object", 632 | "properties" : { 633 | "description" : { 634 | "type" : "string" 635 | }, 636 | "icon" : { 637 | "type" : "string" 638 | }, 639 | "image" : { 640 | "type" : "string" 641 | }, 642 | "title" : { 643 | "type" : "string" 644 | } 645 | } 646 | }, 647 | "LinkGroup" : { 648 | "required" : [ "apiVersion", "kind", "metadata" ], 649 | "type" : "object", 650 | "properties" : { 651 | "apiVersion" : { 652 | "type" : "string" 653 | }, 654 | "kind" : { 655 | "type" : "string" 656 | }, 657 | "metadata" : { 658 | "$ref" : "#/components/schemas/Metadata" 659 | }, 660 | "spec" : { 661 | "$ref" : "#/components/schemas/LinkGroupSpec" 662 | } 663 | } 664 | }, 665 | "LinkGroupList" : { 666 | "required" : [ "first", "hasNext", "hasPrevious", "items", "last", "page", "size", "total", "totalPages" ], 667 | "type" : "object", 668 | "properties" : { 669 | "first" : { 670 | "type" : "boolean", 671 | "description" : "Indicates whether current page is the first page." 672 | }, 673 | "hasNext" : { 674 | "type" : "boolean", 675 | "description" : "Indicates whether current page has previous page." 676 | }, 677 | "hasPrevious" : { 678 | "type" : "boolean", 679 | "description" : "Indicates whether current page has previous page." 680 | }, 681 | "items" : { 682 | "type" : "array", 683 | "description" : "A chunk of items.", 684 | "items" : { 685 | "$ref" : "#/components/schemas/LinkGroup" 686 | } 687 | }, 688 | "last" : { 689 | "type" : "boolean", 690 | "description" : "Indicates whether current page is the last page." 691 | }, 692 | "page" : { 693 | "type" : "integer", 694 | "description" : "Page number, starts from 1. If not set or equal to 0, it means no pagination.", 695 | "format" : "int32" 696 | }, 697 | "size" : { 698 | "type" : "integer", 699 | "description" : "Size of each page. If not set or equal to 0, it means no pagination.", 700 | "format" : "int32" 701 | }, 702 | "total" : { 703 | "type" : "integer", 704 | "description" : "Total elements.", 705 | "format" : "int64" 706 | }, 707 | "totalPages" : { 708 | "type" : "integer", 709 | "description" : "Indicates total pages.", 710 | "format" : "int64" 711 | } 712 | } 713 | }, 714 | "LinkGroupSpec" : { 715 | "required" : [ "displayName" ], 716 | "type" : "object", 717 | "properties" : { 718 | "displayName" : { 719 | "type" : "string" 720 | }, 721 | "links" : { 722 | "type" : "array", 723 | "description" : "Names of links below this group.", 724 | "deprecated" : true, 725 | "items" : { 726 | "type" : "string", 727 | "description" : "Names of links below this group.", 728 | "deprecated" : true 729 | } 730 | }, 731 | "priority" : { 732 | "type" : "integer", 733 | "format" : "int32" 734 | } 735 | } 736 | }, 737 | "LinkList" : { 738 | "required" : [ "first", "hasNext", "hasPrevious", "items", "last", "page", "size", "total", "totalPages" ], 739 | "type" : "object", 740 | "properties" : { 741 | "first" : { 742 | "type" : "boolean", 743 | "description" : "Indicates whether current page is the first page." 744 | }, 745 | "hasNext" : { 746 | "type" : "boolean", 747 | "description" : "Indicates whether current page has previous page." 748 | }, 749 | "hasPrevious" : { 750 | "type" : "boolean", 751 | "description" : "Indicates whether current page has previous page." 752 | }, 753 | "items" : { 754 | "type" : "array", 755 | "description" : "A chunk of items.", 756 | "items" : { 757 | "$ref" : "#/components/schemas/Link" 758 | } 759 | }, 760 | "last" : { 761 | "type" : "boolean", 762 | "description" : "Indicates whether current page is the last page." 763 | }, 764 | "page" : { 765 | "type" : "integer", 766 | "description" : "Page number, starts from 1. If not set or equal to 0, it means no pagination.", 767 | "format" : "int32" 768 | }, 769 | "size" : { 770 | "type" : "integer", 771 | "description" : "Size of each page. If not set or equal to 0, it means no pagination.", 772 | "format" : "int32" 773 | }, 774 | "total" : { 775 | "type" : "integer", 776 | "description" : "Total elements.", 777 | "format" : "int64" 778 | }, 779 | "totalPages" : { 780 | "type" : "integer", 781 | "description" : "Indicates total pages.", 782 | "format" : "int64" 783 | } 784 | } 785 | }, 786 | "LinkSpec" : { 787 | "required" : [ "displayName", "url" ], 788 | "type" : "object", 789 | "properties" : { 790 | "description" : { 791 | "type" : "string" 792 | }, 793 | "displayName" : { 794 | "type" : "string" 795 | }, 796 | "groupName" : { 797 | "type" : "string" 798 | }, 799 | "logo" : { 800 | "type" : "string" 801 | }, 802 | "priority" : { 803 | "type" : "integer", 804 | "format" : "int32" 805 | }, 806 | "url" : { 807 | "type" : "string" 808 | } 809 | } 810 | }, 811 | "Metadata" : { 812 | "required" : [ "name" ], 813 | "type" : "object", 814 | "properties" : { 815 | "annotations" : { 816 | "type" : "object", 817 | "additionalProperties" : { 818 | "type" : "string" 819 | } 820 | }, 821 | "creationTimestamp" : { 822 | "type" : "string", 823 | "format" : "date-time", 824 | "nullable" : true 825 | }, 826 | "deletionTimestamp" : { 827 | "type" : "string", 828 | "format" : "date-time", 829 | "nullable" : true 830 | }, 831 | "finalizers" : { 832 | "uniqueItems" : true, 833 | "type" : "array", 834 | "nullable" : true, 835 | "items" : { 836 | "type" : "string", 837 | "nullable" : true 838 | } 839 | }, 840 | "generateName" : { 841 | "type" : "string", 842 | "description" : "The name field will be generated automatically according to the given generateName field" 843 | }, 844 | "labels" : { 845 | "type" : "object", 846 | "additionalProperties" : { 847 | "type" : "string" 848 | } 849 | }, 850 | "name" : { 851 | "type" : "string", 852 | "description" : "Metadata name" 853 | }, 854 | "version" : { 855 | "type" : "integer", 856 | "format" : "int64", 857 | "nullable" : true 858 | } 859 | } 860 | }, 861 | "MoveOperation" : { 862 | "required" : [ "op", "from", "path" ], 863 | "type" : "object", 864 | "properties" : { 865 | "from" : { 866 | "pattern" : "^(/[^/~]*(~[01][^/~]*)*)*$", 867 | "type" : "string", 868 | "description" : "A JSON Pointer path pointing to the location to move/copy from.", 869 | "example" : "/a/b/c" 870 | }, 871 | "op" : { 872 | "type" : "string", 873 | "enum" : [ "move" ] 874 | }, 875 | "path" : { 876 | "pattern" : "^(/[^/~]*(~[01][^/~]*)*)*$", 877 | "type" : "string", 878 | "description" : "A JSON Pointer path pointing to the location to move/copy from.", 879 | "example" : "/a/b/c" 880 | } 881 | } 882 | }, 883 | "RemoveOperation" : { 884 | "required" : [ "op", "path" ], 885 | "type" : "object", 886 | "properties" : { 887 | "op" : { 888 | "type" : "string", 889 | "enum" : [ "remove" ] 890 | }, 891 | "path" : { 892 | "pattern" : "^(/[^/~]*(~[01][^/~]*)*)*$", 893 | "type" : "string", 894 | "description" : "A JSON Pointer path pointing to the location to move/copy from.", 895 | "example" : "/a/b/c" 896 | } 897 | } 898 | }, 899 | "ReplaceOperation" : { 900 | "required" : [ "op", "path", "value" ], 901 | "type" : "object", 902 | "properties" : { 903 | "op" : { 904 | "type" : "string", 905 | "enum" : [ "replace" ] 906 | }, 907 | "path" : { 908 | "pattern" : "^(/[^/~]*(~[01][^/~]*)*)*$", 909 | "type" : "string", 910 | "description" : "A JSON Pointer path pointing to the location to move/copy from.", 911 | "example" : "/a/b/c" 912 | }, 913 | "value" : { 914 | "description" : "Value can be any JSON value" 915 | } 916 | } 917 | }, 918 | "TestOperation" : { 919 | "required" : [ "op", "path", "value" ], 920 | "type" : "object", 921 | "properties" : { 922 | "op" : { 923 | "type" : "string", 924 | "enum" : [ "test" ] 925 | }, 926 | "path" : { 927 | "pattern" : "^(/[^/~]*(~[01][^/~]*)*)*$", 928 | "type" : "string", 929 | "description" : "A JSON Pointer path pointing to the location to move/copy from.", 930 | "example" : "/a/b/c" 931 | }, 932 | "value" : { 933 | "description" : "Value can be any JSON value" 934 | } 935 | } 936 | } 937 | }, 938 | "securitySchemes" : { 939 | "basicAuth" : { 940 | "scheme" : "basic", 941 | "type" : "http" 942 | }, 943 | "bearerAuth" : { 944 | "bearerFormat" : "JWT", 945 | "scheme" : "bearer", 946 | "type" : "http" 947 | } 948 | } 949 | } 950 | } --------------------------------------------------------------------------------