部署文件和插件问题修复

This commit is contained in:
王一之 2024-03-22 20:31:04 +08:00
parent fab9914f39
commit c956a8dfed
20 changed files with 595 additions and 24 deletions

View File

@ -0,0 +1,95 @@
name: Release
on:
push:
branches:
- main
env:
APP_NAME: blog
NAMESPACE: app
REGISTRY: ${{ secrets.DOCKER_REGISTRY && secrets.DOCKER_REGISTRY || 'docker.io' }}
REPOSITORY: ${{ github.repository }}
DOMAIN: blog.icodef.com
ENV: ${{ startsWith(github.ref, 'refs/heads/release/') && 'pre' || startsWith(github.ref, 'refs/heads/test/') && 'test' || github.ref=='refs/heads/main' && 'prod' }}
RUNNER_TOOL_CACHE: /toolcache
BASEIMAGE: ${{ secrets.BASEIMAGE && secrets.BASEIMAGE || '' }}
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Use Node.js
uses: actions/setup-node@v4.1.1
with:
node-version: "20.x"
cache: 'npm'
- name: Build
run: |
npm ci
npm run build
- name: Set up QEMU
# uses: docker/setup-qemu-action@v3
uses: actions/setup-qemu-action@v3
- name: Set up Docker Buildx
# uses: docker/setup-buildx-action@v3
uses: actions/setup-buildx-action@v3
- name: Login to Docker Hub
# uses: docker/login-action@v3
uses: actions/login-action@v1
with:
registry: ${{ env.REGISTRY }}
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_TOKEN }}
- name: Set outputs
id: vars
run: |
echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- name: Docker build and push
# use: docker/build-push-action@v5
uses: actions/build-push-action@v5
with:
push: true
file: deploy/docker/Dockerfile
tags: ${{ env.REGISTRY }}/${{ env.REPOSITORY }}:${{ steps.vars.outputs.sha_short }}
context: .
build-args: |
BASEIMAGE=${{ env.BASEIMAGE }}/nginx:1.25
- name: Set up kubeconfig
# uses: azure/k8s-set-context@v3
uses: actions/k8s-set-context@v3
with:
method: kubeconfig
kubeconfig: ${{ secrets.KUBE_CONFIG }}
context: k8s-context
- name: Set up Helm
# uses: azure/setup-helm@v3
uses: actions/setup-helm@v3.6
with:
version: 'v3.13.1' # default is latest (stable)
- name: Deploy ${{ env.ENV }}
env:
RESOURCE_CPU: ${{ env.ENV=='prod' && '100m' || '50m' }}
RESOURCE_MEMORY: ${{ env.ENV=='prod' && '256Mi' || '128Mi' }}
run: |
cd deploy/helm
helm upgrade --install \
--namespace $NAMESPACE $APP_NAME . \
-f values.yaml \
--set image.tag=${{ steps.vars.outputs.sha_short }} \
--set image.repository=$REGISTRY/$REPOSITORY \
--set resources.requests.cpu=$RESOURCE_CPU \
--set resources.requests.memory=$RESOURCE_MEMORY

10
.idea/.gitignore generated vendored Normal file
View File

@ -0,0 +1,10 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
# GitHub Copilot persisted chat sessions
/copilot/chatSessions

5
deploy/docker/Dockerfile Normal file
View File

@ -0,0 +1,5 @@
ARG BASEIMAGE
FROM $BASEIMAGE
COPY build /usr/share/nginx/html

23
deploy/helm/.helmignore Normal file
View File

@ -0,0 +1,23 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/

24
deploy/helm/Chart.yaml Normal file
View File

@ -0,0 +1,24 @@
apiVersion: v2
name: blog
description: A Helm chart for Kubernetes
# A chart can be either an 'application' or a 'library' chart.
#
# Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed.
#
# Library charts provide useful utilities or functions for the chart developer. They're included as
# a dependency of application charts to inject those utilities and functions into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 1.0.0
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
appVersion: "1.16.0"

View File

@ -0,0 +1,22 @@
1. Get the application URL by running these commands:
{{- if .Values.ingress.enabled }}
{{- range $host := .Values.ingress.hosts }}
{{- range .paths }}
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
{{- end }}
{{- end }}
{{- else if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "scriptcat-docs.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "scriptcat-docs.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "scriptcat-docs.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else if contains "ClusterIP" .Values.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "scriptcat-docs.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
{{- end }}

View File

@ -0,0 +1,62 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "scriptcat-docs.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "scriptcat-docs.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "scriptcat-docs.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "scriptcat-docs.labels" -}}
helm.sh/chart: {{ include "scriptcat-docs.chart" . }}
{{ include "scriptcat-docs.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "scriptcat-docs.selectorLabels" -}}
app.kubernetes.io/name: {{ include "scriptcat-docs.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "scriptcat-docs.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "scriptcat-docs.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}

View File

@ -0,0 +1,61 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "scriptcat-docs.fullname" . }}
labels:
{{- include "scriptcat-docs.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "scriptcat-docs.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "scriptcat-docs.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "scriptcat-docs.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: 80
protocol: TCP
livenessProbe:
httpGet:
path: /
port: http
readinessProbe:
httpGet:
path: /
port: http
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}

View File

@ -0,0 +1,28 @@
{{- if .Values.autoscaling.enabled }}
apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
name: {{ include "scriptcat-docs.fullname" . }}
labels:
{{- include "scriptcat-docs.labels" . | nindent 4 }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: {{ include "scriptcat-docs.fullname" . }}
minReplicas: {{ .Values.autoscaling.minReplicas }}
maxReplicas: {{ .Values.autoscaling.maxReplicas }}
metrics:
{{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
- type: Resource
resource:
name: cpu
targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
{{- end }}
{{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
- type: Resource
resource:
name: memory
targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
{{- end }}
{{- end }}

View File

@ -0,0 +1,61 @@
{{- if .Values.ingress.enabled -}}
{{- $fullName := include "scriptcat-docs.fullname" . -}}
{{- $svcPort := .Values.service.port -}}
{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }}
{{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }}
{{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}}
{{- end }}
{{- end }}
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1
{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1beta1
{{- else -}}
apiVersion: extensions/v1beta1
{{- end }}
kind: Ingress
metadata:
name: {{ $fullName }}
labels:
{{- include "scriptcat-docs.labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
ingressClassName: {{ .Values.ingress.className }}
{{- end }}
{{- if .Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
{{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }}
pathType: {{ .pathType }}
{{- end }}
backend:
{{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
service:
name: {{ $fullName }}
port:
number: {{ $svcPort }}
{{- else }}
serviceName: {{ $fullName }}
servicePort: {{ $svcPort }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}

View File

@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "scriptcat-docs.fullname" . }}
labels:
{{- include "scriptcat-docs.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
{{- include "scriptcat-docs.selectorLabels" . | nindent 4 }}

View File

@ -0,0 +1,12 @@
{{- if .Values.serviceAccount.create -}}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "scriptcat-docs.serviceAccountName" . }}
labels:
{{- include "scriptcat-docs.labels" . | nindent 4 }}
{{- with .Values.serviceAccount.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}

View File

@ -0,0 +1,15 @@
apiVersion: v1
kind: Pod
metadata:
name: "{{ include "scriptcat-docs.fullname" . }}-test-connection"
labels:
{{- include "scriptcat-docs.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": test
spec:
containers:
- name: wget
image: busybox
command: ['wget']
args: ['{{ include "scriptcat-docs.fullname" . }}:{{ .Values.service.port }}']
restartPolicy: Never

89
deploy/helm/values.yaml Normal file
View File

@ -0,0 +1,89 @@
# Default values for scriptcat-docs.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
replicaCount: 1
image:
repository: nginx
pullPolicy: IfNotPresent
# Overrides the image tag whose default is the chart appVersion.
tag: ""
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
serviceAccount:
# Specifies whether a service account should be created
create: false
# Annotations to add to the service account
annotations: {}
# The name of the service account to use.
# If not set and create is true, a name is generated using the fullname template
name: ""
podAnnotations: {}
podSecurityContext: {}
# fsGroup: 2000
securityContext: {}
# capabilities:
# drop:
# - ALL
# readOnlyRootFilesystem: true
# runAsNonRoot: true
# runAsUser: 1000
service:
type: ClusterIP
port: 80
ingress:
enabled: true
className: "k3s-backup-nginx"
annotations:
# kubernetes.io/ingress.class: nginx
# kubernetes.io/tls-acme: "true"
nginx.ingress.kubernetes.io/configuration-snippet: |
rewrite ^/use$ /docs/use/ redirect; # 重定向 /use
rewrite ^/use/$ /docs/use/ redirect; # 重定向 /use
rewrite ^/dev/(.*)$ /docs/dev/$1 redirect; # 重定向 /dev
rewrite ^/dev$ /docs/dev/ redirect; # 重定向 /dev
rewrite ^/change/(.*)$ /docs/change/$1 redirect; # 重定向 /change
rewrite ^/change$ /docs/change/ redirect; # 重定向 /change
hosts:
- host: docs.scriptcat.org
paths:
- path: /
pathType: ImplementationSpecific
tls:
- secretName: scriptcat-org-tls
hosts:
- docs.scriptcat.org
resources: {}
# We usually recommend not to specify default resources and to leave this as a conscious
# choice for the user. This also increases chances charts run on environments with little
# resources, such as Minikube. If you do want to specify resources, uncomment the following
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
# limits:
# cpu: 100m
# memory: 128Mi
# requests:
# cpu: 100m
# memory: 128Mi
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 100
targetCPUUtilizationPercentage: 80
# targetMemoryUtilizationPercentage: 80
nodeSelector: {}
tolerations: []
affinity: {}

View File

@ -2,9 +2,7 @@
title: Go Slice(切片) title: Go Slice(切片)
--- ---
> 感觉切片只要知道底层是引用的一个数组对象,就挺好理解了.这里写下一些笔记,方便记忆和以后再来查找. ## 切片和数组
### 切片和数组
切片由三个部分组成:指针(指向底层数组),长度(当前切片使用的长度),容量(切片能包含多少个成员) 切片由三个部分组成:指针(指向底层数组),长度(当前切片使用的长度),容量(切片能包含多少个成员)
@ -43,7 +41,7 @@ func ModifySlice(slice []int) {
等看完下面再写另外一个情况,在函数里面,给切片增加成员,会怎么样? 等看完下面再写另外一个情况,在函数里面,给切片增加成员,会怎么样?
### 切片 ## 切片
定义一个数组和定义一个切片的区别是[...]和\[\](当然还有其他的定义方式) 定义一个数组和定义一个切片的区别是[...]和\[\](当然还有其他的定义方式)
@ -103,7 +101,7 @@ func Test_ModifyTwoSlice(t *testing.T) {
一样的全部一起修改成功了 一样的全部一起修改成功了
### append ## append
然后我们来看看 append 然后我们来看看 append
@ -166,7 +164,7 @@ func ModifySlice(slice []int) {
我把之前的`ModifySlice`方法修改了一下,然后成员没加,后面再修改回去为 3 也没有发生变化了. 我把之前的`ModifySlice`方法修改了一下,然后成员没加,后面再修改回去为 3 也没有发生变化了.
这是因为 append 的时候因为容量不够扩容了,导致底层数组指针发生了改变,但是传进来的切片是外面切片的副本,修改这个切片里面的数组指针不会影响到外面的切片 这是因为 append 的时候因为容量不够扩容了,导致底层数组指针发生了改变,但是传进来的切片是外面切片的副本,修改这个切片里面的数组指针不会影响到外面的切片
#### 奇淫巧技 ## 奇淫巧技
例如这个(go 圣经里面抄的 233),reverse 的参数是切片,但是我们需要处理的是一个数组,这里我们就可以直接用`arr[:]`把数组转化为切片进行处理 例如这个(go 圣经里面抄的 233),reverse 的参数是切片,但是我们需要处理的是一个数组,这里我们就可以直接用`arr[:]`把数组转化为切片进行处理

View File

@ -0,0 +1,3 @@
# Golang IO包
某些时候需要对

View File

@ -2,15 +2,19 @@ import docsPlugin, {
PluginOptions, PluginOptions,
LoadedContent, LoadedContent,
DocMetadata as OfficialDocMetadata, DocMetadata as OfficialDocMetadata,
Options,
} from "@docusaurus/plugin-content-docs"; } from "@docusaurus/plugin-content-docs";
import { Plugin, LoadContext } from "@docusaurus/types"; import {
Plugin,
LoadContext,
OptionValidationContext,
} from "@docusaurus/types";
import matter from "gray-matter"; import matter from "gray-matter";
//@ts-ignore
export { validateOptions } from "@docusaurus/plugin-content-docs/src/index";
import path from "path"; import path from "path";
import fs from "fs";
import simpleGit, { DefaultLogFields } from "simple-git"; import simpleGit, { DefaultLogFields } from "simple-git";
import readingTime, { ReadTimeResults } from "reading-time"; import readingTime, { ReadTimeResults } from "reading-time";
//@ts-ignore
import { validateOptions as officialValidateOptions } from "@docusaurus/plugin-content-docs/src/index";
export type DocMetadata = OfficialDocMetadata & { detail: Detail }; export type DocMetadata = OfficialDocMetadata & { detail: Detail };
@ -30,6 +34,7 @@ export default async function pluginContentDocs(
context, context,
options options
)) as Plugin<LoadedContent>; )) as Plugin<LoadedContent>;
const isProd = process.env.NODE_ENV === "production";
const themePath = path.resolve(__dirname, "./theme"); const themePath = path.resolve(__dirname, "./theme");
ret.getThemePath = () => { ret.getThemePath = () => {
@ -57,6 +62,13 @@ export default async function pluginContentDocs(
filename: filename, filename: filename,
reading_time: readingTime(meta.content), reading_time: readingTime(meta.content),
}; };
if (!isProd && !options.debug) {
// 非生产、非debug模式 直接生产假数据
detail.create_date = new Date();
detail.update_date = new Date();
doc.detail = detail;
return;
}
return new Promise<void>((resolve) => { return new Promise<void>((resolve) => {
// 读取git log文件时间 // 读取git log文件时间
git.log<DefaultLogFields>( git.log<DefaultLogFields>(
@ -104,3 +116,17 @@ export default async function pluginContentDocs(
return ret; return ret;
} }
export function validateOptions({
validate,
options: userOptions,
}: OptionValidationContext<Options, PluginOptions>): PluginOptions {
// @ts-ignore
const oldDebug = userOptions.debug;
//@ts-ignore
delete userOptions.debug;
const ret = officialValidateOptions({ validate, options: userOptions });
// @ts-ignore
userOptions.debug = oldDebug;
return ret;
}

View File

@ -44,10 +44,7 @@ export interface Articles {
total: number; total: number;
} }
export default function ( export default function (context: LoadContext, options: any): Plugin {
context: LoadContext,
options: { debug?: boolean }
): Plugin {
const themePath = path.resolve(__dirname, "./theme"); const themePath = path.resolve(__dirname, "./theme");
return { return {
name: "docusaurus-plugin-docs-info", name: "docusaurus-plugin-docs-info",
@ -56,15 +53,6 @@ export default function (
}, },
async contentLoaded({ content, actions, allContent }): Promise<void> { async contentLoaded({ content, actions, allContent }): Promise<void> {
const { addRoute, createData, setGlobalData } = actions; const { addRoute, createData, setGlobalData } = actions;
const isProd = process.env.NODE_ENV === "production";
if (!isProd && !options.debug) {
setGlobalData({
current: 1,
list: [],
total: 0,
});
return;
}
const docsData = allContent["docusaurus-plugin-content-docs"] as { const docsData = allContent["docusaurus-plugin-content-docs"] as {
default: { loadedVersions: LoadedVersion }; default: { loadedVersions: LoadedVersion };

View File

@ -25,7 +25,7 @@ import dayjs from "dayjs";
- user doesn't ask to hide it with front matter - user doesn't ask to hide it with front matter
- the markdown content does not already contain a top-level h1 heading - the markdown content does not already contain a top-level h1 heading
*/ */
function useSyntheticTitle(): string | null { export function useSyntheticTitle(): string | null {
const { metadata, frontMatter, contentTitle } = useDoc(); const { metadata, frontMatter, contentTitle } = useDoc();
const shouldRender = const shouldRender =
!frontMatter.hide_title && typeof contentTitle === "undefined"; !frontMatter.hide_title && typeof contentTitle === "undefined";

View File

@ -0,0 +1,34 @@
import React from "react";
import Heading from "@theme-original/Heading";
import { useSyntheticTitle } from "../DocItem/Content";
import { useDoc } from "@docusaurus/theme-common/internal";
import dayjs from "dayjs";
import { Detail } from "docusaurus-plugin-content-docs-ex/src";
export default function HeadingWrapper(props) {
const syntheticTitle = useSyntheticTitle();
const doc = useDoc();
const detail = (doc.metadata as any).detail as Detail;
return (
<>
<Heading {...props}>
{props.children}
{detail && !syntheticTitle && (
<span
style={{
display: "block",
fontSize: "14px",
fontWeight: "normal",
marginBottom: "10px",
marginTop: "10px",
}}
>
{dayjs(detail.create_date).format("YYYY年MM月DD日")} · {" "}
{Math.ceil(detail.reading_time.minutes)}
</span>
)}
</Heading>
</>
);
}