Merge remote-tracking branch 'origin/master' into feature/workflow
This commit is contained in:
@@ -2,52 +2,67 @@ name: 全局K3s部署
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
# ========== 核心修复:替换为具体Ubuntu版本,解决运行期匹配问题 ==========
|
||||
runs-on: ubuntu-24.04
|
||||
env:
|
||||
K3S_HOST: 121.37.117.181
|
||||
# 从组织级Secrets读取,不用在仓库重复配置
|
||||
K3S_HOST: ${{ secrets.K3S_HOST }}
|
||||
APP_NAME: ${{ gitea.repo_name }}
|
||||
REGISTRY: 你的镜像仓库地址 # 比如 docker.io/你的用户名
|
||||
# 补充:若后续要推送镜像,需替换为实际镜像仓库地址(比如你的Gitea镜像仓库)
|
||||
REGISTRY: 116.204.74.41:3000/red-future
|
||||
steps:
|
||||
- uses: gitea/actions/checkout@v4
|
||||
# ========== 核心:新增国内Git代理,彻底解决GitHub拉取慢 ==========
|
||||
- name: 配置国内GitHub代理加速
|
||||
run: |
|
||||
# 全局Git代理:所有GitHub请求走国内镜像站
|
||||
git config --global url."https://ghproxy.com/https://github.com/".insteadOf "https://github.com/"
|
||||
# 可选:替换Ubuntu源为清华源,加速依赖安装
|
||||
sed -i 's/archive.ubuntu.com/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list
|
||||
apt update -y
|
||||
# ========== 核心修改:替换checkout源,避开GitHub ==========
|
||||
- name: 拉取代码(Gitea官方源)
|
||||
uses: gitea/actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # 可选:拉取完整历史,加速后续操作
|
||||
timeout-minutes: 10 # 增加超时,避免拉取中断
|
||||
|
||||
# 1. 初始化 Docker Buildx
|
||||
# 1. 初始化 Docker Buildx(原内容不变)
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
# 2. 登录镜像仓库(按需)
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USER }}
|
||||
password: ${{ secrets.DOCKER_PWD }}
|
||||
# 2. 可选:登录镜像仓库(若需推送镜像,取消注释并配置密钥)
|
||||
# - name: Login to Gitea Registry
|
||||
# uses: docker/login-action@v3
|
||||
# with:
|
||||
# registry: 116.204.74.41:3000
|
||||
# username: ${{ secrets.GITEA_USER }}
|
||||
# password: ${{ secrets.GITEA_PWD }}
|
||||
|
||||
# 3. 构建+推送,启用缓存
|
||||
# 3. 构建+推送(原内容不变)
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: ${{ env.REGISTRY }}/${{ env.APP_NAME }}:${{ gitea.sha }}
|
||||
# 缓存配置:推送到镜像仓库
|
||||
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.APP_NAME }}:buildcache
|
||||
cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.APP_NAME }}:buildcache,mode=max
|
||||
|
||||
# 4. 核心修改:先上传deploy.yaml到K3s服务器,再执行kubectl
|
||||
# 4. 修复后的SSH部署步骤(解决路径+命名空间问题)
|
||||
- name: SSH部署K3s
|
||||
run:
|
||||
run: |
|
||||
mkdir -p ~/.ssh
|
||||
echo "${{ secrets.K3S_PEM_KEY }}" > k3s.pem
|
||||
chmod 600 k3s.pem
|
||||
# 关键1:把Gitea仓库里的deploy.yaml上传到K3s服务器临时目录(/tmp)
|
||||
# 注意:如果你的deploy.yaml不在仓库根目录,要修改./deploy.yaml为实际路径
|
||||
|
||||
# ========== 修正1:上传仓库根目录的deploy.yaml到K3s临时目录 ==========
|
||||
scp -i k3s.pem -o StrictHostKeyChecking=no ./deploy.yaml root@${K3S_HOST}:/tmp/
|
||||
# 关键2:执行kubectl时指向临时目录的文件,而非不存在的/k8s/
|
||||
|
||||
# ========== 修正2:kubectl指向临时文件+补充命名空间 ==========
|
||||
ssh -i k3s.pem -o StrictHostKeyChecking=no root@${K3S_HOST} << CMD
|
||||
kubectl apply -f /tmp/deploy.yaml
|
||||
kubectl rollout restart deployment ${APP_NAME} -n default
|
||||
# 可选:部署完成后删除临时文件,清理服务器
|
||||
rm -f /tmp/deploy.yaml
|
||||
rm -f /tmp/deploy.yaml # 可选:清理临时文件
|
||||
CMD
|
||||
@@ -1,12 +1,6 @@
|
||||
FROM gitea/gitea:latest
|
||||
# 拷贝预设工作流模板到容器内仓库模板目录
|
||||
COPY ./workflow_template/.gitea /data/gitea/templates/repo/.gitea
|
||||
|
||||
# ==================== 第一阶段:构建前端 ====================
|
||||
FROM node:18-alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# 配置Alpine国内镜像源
|
||||
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
|
||||
|
||||
|
||||
44
deploy.yaml
Normal file
44
deploy.yaml
Normal file
@@ -0,0 +1,44 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: ${APP_NAME}
|
||||
namespace: default
|
||||
labels:
|
||||
app: ${APP_NAME}
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: ${APP_NAME}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: ${APP_NAME}
|
||||
spec:
|
||||
containers:
|
||||
- name: ${APP_NAME}
|
||||
image: ${REGISTRY}/${APP_NAME}:${gitea.sha}
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- containerPort: 80 # 你的项目实际端口(比如前端80、后端8080)
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: ${APP_NAME}-service
|
||||
namespace: default
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: ${APP_NAME}
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
nodePort: 30001 # 必须在30000-32767之间
|
||||
16
src/api/system/pwconfig/index.ts
Normal file
16
src/api/system/pwconfig/index.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import request from '/@/utils/request';
|
||||
|
||||
export function getPwConfig() {
|
||||
return request({
|
||||
url: '/admin-go/api/v1/system/pwconfig/get',
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
export function savePwConfig(data: any) {
|
||||
return request({
|
||||
url: '/admin-go/api/v1/system/pwconfig/save',
|
||||
method: 'post',
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
217
src/views/system/pwconfig/index.vue
Normal file
217
src/views/system/pwconfig/index.vue
Normal file
@@ -0,0 +1,217 @@
|
||||
<template>
|
||||
<div class="system-pwconfig-container">
|
||||
<el-card shadow="hover">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>密码策略配置</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-form :model="ruleForm" ref="formRef" :rules="rules" label-width="180px" style="max-width: 800px;">
|
||||
<el-form-item label="启用密码策略" prop="enabled">
|
||||
<el-switch v-model="ruleForm.enabled" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="最小密码长度" prop="minLength">
|
||||
<el-input-number v-model="ruleForm.minLength" :min="4" :max="32" placeholder="请输入最小密码长度" />
|
||||
<span class="ml10 text-muted">位</span>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="最大密码长度" prop="maxLength">
|
||||
<el-input-number v-model="ruleForm.maxLength" :min="4" :max="128" placeholder="请输入最大密码长度" />
|
||||
<span class="ml10 text-muted">位</span>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="必须包含大写字母" prop="requireUppercase">
|
||||
<el-switch v-model="ruleForm.requireUppercase" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="必须包含小写字母" prop="requireLowercase">
|
||||
<el-switch v-model="ruleForm.requireLowercase" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="必须包含数字" prop="requireDigit">
|
||||
<el-switch v-model="ruleForm.requireDigit" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="必须包含特殊字符" prop="requireSpecialChar">
|
||||
<el-switch v-model="ruleForm.requireSpecialChar" />
|
||||
<div class="text-muted mt5" style="font-size: 12px;">特殊字符包括:!@#$%^&*()_+-=[]{}|;:,.<>?</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="密码过期天数" prop="expireDays">
|
||||
<el-input-number v-model="ruleForm.expireDays" :min="0" :max="365" placeholder="请输入密码过期天数" />
|
||||
<span class="ml10 text-muted">天(0表示永不过期)</span>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="禁止重复使用次数" prop="historyLimit">
|
||||
<el-input-number v-model="ruleForm.historyLimit" :min="0" :max="24" placeholder="请输入禁止重复使用次数" />
|
||||
<span class="ml10 text-muted">次(0表示不限制)</span>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="登录失败锁定次数" prop="maxRetryCount">
|
||||
<el-input-number v-model="ruleForm.maxRetryCount" :min="0" :max="10" placeholder="请输入登录失败锁定次数" />
|
||||
<span class="ml10 text-muted">次(0表示不锁定)</span>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="锁定时长" prop="lockTimeMinutes">
|
||||
<el-input-number v-model="ruleForm.lockTimeMinutes" :min="1" :max="1440" placeholder="请输入锁定时长" />
|
||||
<span class="ml10 text-muted">分钟</span>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="ruleForm.remark" type="textarea" :rows="3" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="onSubmit" :loading="loading">保存配置</el-button>
|
||||
<el-button @click="resetForm">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { reactive, toRefs, defineComponent, ref, onMounted, unref } from 'vue';
|
||||
import { ElMessage, FormInstance, FormRules } from 'element-plus';
|
||||
import { getPwConfig, savePwConfig } from "/@/api/system/pwconfig";
|
||||
|
||||
interface RuleFormState {
|
||||
enabled: boolean;
|
||||
minLength: number;
|
||||
maxLength: number;
|
||||
requireUppercase: boolean;
|
||||
requireLowercase: boolean;
|
||||
requireDigit: boolean;
|
||||
requireSpecialChar: boolean;
|
||||
expireDays: number;
|
||||
historyLimit: number;
|
||||
maxRetryCount: number;
|
||||
lockTimeMinutes: number;
|
||||
remark: string;
|
||||
}
|
||||
|
||||
interface PwConfigState {
|
||||
ruleForm: RuleFormState;
|
||||
rules: FormRules;
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'systemPwConfig',
|
||||
setup() {
|
||||
const formRef = ref<FormInstance>();
|
||||
const state = reactive<PwConfigState>({
|
||||
loading: false,
|
||||
ruleForm: {
|
||||
enabled: false,
|
||||
minLength: 8,
|
||||
maxLength: 32,
|
||||
requireUppercase: false,
|
||||
requireLowercase: true,
|
||||
requireDigit: true,
|
||||
requireSpecialChar: false,
|
||||
expireDays: 90,
|
||||
historyLimit: 5,
|
||||
maxRetryCount: 5,
|
||||
lockTimeMinutes: 30,
|
||||
remark: '',
|
||||
},
|
||||
rules: {
|
||||
minLength: [
|
||||
{ required: true, message: '请输入最小密码长度', trigger: 'blur' }
|
||||
],
|
||||
maxLength: [
|
||||
{ required: true, message: '请输入最大密码长度', trigger: 'blur' }
|
||||
],
|
||||
expireDays: [
|
||||
{ required: true, message: '请输入密码过期天数', trigger: 'blur' }
|
||||
],
|
||||
historyLimit: [
|
||||
{ required: true, message: '请输入禁止重复使用次数', trigger: 'blur' }
|
||||
],
|
||||
maxRetryCount: [
|
||||
{ required: true, message: '请输入登录失败锁定次数', trigger: 'blur' }
|
||||
],
|
||||
lockTimeMinutes: [
|
||||
{ required: true, message: '请输入锁定时长', trigger: 'blur' }
|
||||
],
|
||||
}
|
||||
});
|
||||
|
||||
// 获取配置
|
||||
const loadConfig = async () => {
|
||||
try {
|
||||
const res: any = await getPwConfig();
|
||||
if (res.code === 0 && res.data) {
|
||||
state.ruleForm = {
|
||||
...state.ruleForm,
|
||||
...res.data,
|
||||
};
|
||||
}
|
||||
} catch {
|
||||
// 错误由全局拦截器处理
|
||||
}
|
||||
};
|
||||
|
||||
// 重置表单
|
||||
const resetForm = () => {
|
||||
loadConfig();
|
||||
};
|
||||
|
||||
// 保存配置
|
||||
const onSubmit = async () => {
|
||||
const formWrap = unref(formRef);
|
||||
if (!formWrap) return;
|
||||
|
||||
await formWrap.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
// 验证最小长度不大于最大长度
|
||||
if (state.ruleForm.minLength > state.ruleForm.maxLength) {
|
||||
ElMessage.error('最小密码长度不能大于最大密码长度');
|
||||
return;
|
||||
}
|
||||
|
||||
state.loading = true;
|
||||
try {
|
||||
await savePwConfig(state.ruleForm);
|
||||
ElMessage.success('保存成功');
|
||||
} finally {
|
||||
state.loading = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
loadConfig();
|
||||
});
|
||||
|
||||
return {
|
||||
formRef,
|
||||
onSubmit,
|
||||
resetForm,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.text-muted {
|
||||
color: var(--el-text-color-placeholder);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.system-pwconfig-container {
|
||||
:deep(.el-card__body) {
|
||||
padding-top: 30px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user