Compare commits
3 Commits
915992d8de
...
fadb4a4ee3
Author | SHA1 | Date |
---|---|---|
wang | fadb4a4ee3 | |
wang | cb10f62c35 | |
wang | fba2428e30 |
|
@ -1,4 +1,2 @@
|
||||||
.idea
|
.idea
|
||||||
node_modules
|
node_modules
|
||||||
__pycache__
|
|
||||||
public
|
|
|
@ -38,6 +38,7 @@
|
||||||
"react-scripts": "^5.0.1",
|
"react-scripts": "^5.0.1",
|
||||||
"recharts": "^2.12.7",
|
"recharts": "^2.12.7",
|
||||||
"redux": "^5.0.1",
|
"redux": "^5.0.1",
|
||||||
|
"socket.io-client": "^4.7.5",
|
||||||
"stream": "^0.0.2",
|
"stream": "^0.0.2",
|
||||||
"swr": "^2.2.5",
|
"swr": "^2.2.5",
|
||||||
"tailwindcss": "^3.4.3",
|
"tailwindcss": "^3.4.3",
|
||||||
|
@ -4009,6 +4010,11 @@
|
||||||
"@sinonjs/commons": "^1.7.0"
|
"@sinonjs/commons": "^1.7.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@socket.io/component-emitter": {
|
||||||
|
"version": "3.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
|
||||||
|
"integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA=="
|
||||||
|
},
|
||||||
"node_modules/@surma/rollup-plugin-off-main-thread": {
|
"node_modules/@surma/rollup-plugin-off-main-thread": {
|
||||||
"version": "2.2.3",
|
"version": "2.2.3",
|
||||||
"resolved": "https://registry.npmmirror.com/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz",
|
"resolved": "https://registry.npmmirror.com/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz",
|
||||||
|
@ -5441,7 +5447,7 @@
|
||||||
},
|
},
|
||||||
"node_modules/@xterm/xterm": {
|
"node_modules/@xterm/xterm": {
|
||||||
"version": "5.5.0",
|
"version": "5.5.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@xterm/xterm/-/xterm-5.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz",
|
||||||
"integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A=="
|
"integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A=="
|
||||||
},
|
},
|
||||||
"node_modules/@xtuc/ieee754": {
|
"node_modules/@xtuc/ieee754": {
|
||||||
|
@ -7865,6 +7871,46 @@
|
||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/engine.io-client": {
|
||||||
|
"version": "6.5.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.4.tgz",
|
||||||
|
"integrity": "sha512-GeZeeRjpD2qf49cZQ0Wvh/8NJNfeXkXXcoGh+F77oEAgo9gUHwT1fCRxSNU+YEEaysOJTnsFHmM5oAcPy4ntvQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@socket.io/component-emitter": "~3.1.0",
|
||||||
|
"debug": "~4.3.1",
|
||||||
|
"engine.io-parser": "~5.2.1",
|
||||||
|
"ws": "~8.17.1",
|
||||||
|
"xmlhttprequest-ssl": "~2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/engine.io-client/node_modules/ws": {
|
||||||
|
"version": "8.17.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
|
||||||
|
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"bufferutil": "^4.0.1",
|
||||||
|
"utf-8-validate": ">=5.0.2"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"bufferutil": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"utf-8-validate": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/engine.io-parser": {
|
||||||
|
"version": "5.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz",
|
||||||
|
"integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/enhanced-resolve": {
|
"node_modules/enhanced-resolve": {
|
||||||
"version": "5.16.0",
|
"version": "5.16.0",
|
||||||
"resolved": "https://registry.npmmirror.com/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz",
|
"resolved": "https://registry.npmmirror.com/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz",
|
||||||
|
@ -16477,6 +16523,32 @@
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/socket.io-client": {
|
||||||
|
"version": "4.7.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.5.tgz",
|
||||||
|
"integrity": "sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@socket.io/component-emitter": "~3.1.0",
|
||||||
|
"debug": "~4.3.2",
|
||||||
|
"engine.io-client": "~6.5.2",
|
||||||
|
"socket.io-parser": "~4.2.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/socket.io-parser": {
|
||||||
|
"version": "4.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
|
||||||
|
"integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
|
||||||
|
"dependencies": {
|
||||||
|
"@socket.io/component-emitter": "~3.1.0",
|
||||||
|
"debug": "~4.3.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/sockjs": {
|
"node_modules/sockjs": {
|
||||||
"version": "0.3.24",
|
"version": "0.3.24",
|
||||||
"resolved": "https://registry.npmmirror.com/sockjs/-/sockjs-0.3.24.tgz",
|
"resolved": "https://registry.npmmirror.com/sockjs/-/sockjs-0.3.24.tgz",
|
||||||
|
@ -18912,6 +18984,14 @@
|
||||||
"resolved": "https://registry.npmmirror.com/xmlchars/-/xmlchars-2.2.0.tgz",
|
"resolved": "https://registry.npmmirror.com/xmlchars/-/xmlchars-2.2.0.tgz",
|
||||||
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
|
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/xmlhttprequest-ssl": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/y18n": {
|
"node_modules/y18n": {
|
||||||
"version": "5.0.8",
|
"version": "5.0.8",
|
||||||
"resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz",
|
"resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz",
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
"react-scripts": "^5.0.1",
|
"react-scripts": "^5.0.1",
|
||||||
"recharts": "^2.12.7",
|
"recharts": "^2.12.7",
|
||||||
"redux": "^5.0.1",
|
"redux": "^5.0.1",
|
||||||
|
"socket.io-client": "^4.7.5",
|
||||||
"stream": "^0.0.2",
|
"stream": "^0.0.2",
|
||||||
"swr": "^2.2.5",
|
"swr": "^2.2.5",
|
||||||
"tailwindcss": "^3.4.3",
|
"tailwindcss": "^3.4.3",
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
import { Terminal } from "@xterm/xterm";
|
||||||
|
import "@xterm/xterm/css/xterm.css";
|
||||||
|
import React, { useEffect, useState, useRef } from 'react';
|
||||||
|
import { useLocation } from 'react-router-dom';
|
||||||
|
import io, { Socket } from "socket.io-client";
|
||||||
|
|
||||||
|
function base64Decode(encoded: string): string {
|
||||||
|
return atob(encoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function WebSSH() {
|
||||||
|
const location = useLocation();
|
||||||
|
const terminalObj = useRef<HTMLDivElement | null>(null);
|
||||||
|
const [terminal, setTerminal] = useState<Terminal | null>(null);
|
||||||
|
const [socket, setSocket] = useState<Socket | null>(null);
|
||||||
|
const [ip, setIp] = useState("");
|
||||||
|
const [port, setPort] = useState("");
|
||||||
|
const [password, setPassword] = useState("");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const params = new URLSearchParams(location.search);
|
||||||
|
const encodedIp = params.get("ip");
|
||||||
|
const encodedPort = params.get("port");
|
||||||
|
const encodedPassword = params.get("password");
|
||||||
|
|
||||||
|
if (encodedIp && encodedPort && encodedPassword) {
|
||||||
|
setIp(base64Decode(encodedIp));
|
||||||
|
setPort(base64Decode(encodedPort));
|
||||||
|
setPassword(base64Decode(encodedPassword));
|
||||||
|
}
|
||||||
|
}, [location]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (terminal === null) {
|
||||||
|
setTerminal(new Terminal());
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (terminal !== null) {
|
||||||
|
terminal.open(terminalObj.current as HTMLDivElement);
|
||||||
|
|
||||||
|
const newSocket = io("ws://36.138.114.105:32087");
|
||||||
|
setSocket(newSocket);
|
||||||
|
|
||||||
|
terminal.onData((data) => {
|
||||||
|
newSocket.emit("input", data);
|
||||||
|
});
|
||||||
|
|
||||||
|
terminal.onSelectionChange(() => {
|
||||||
|
if (terminal.hasSelection()) {
|
||||||
|
navigator.clipboard.writeText(terminal.getSelection());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
setInterval(() => {
|
||||||
|
newSocket.emit("heartbeat");
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
newSocket.on("output", (data: string) => {
|
||||||
|
terminal.write(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
newSocket.disconnect();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [terminal]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (socket && ip && port && password) {
|
||||||
|
socket.emit("connect_ssh", { ip, port, password });
|
||||||
|
}
|
||||||
|
}, [socket, ip, port, password]);
|
||||||
|
|
||||||
|
function pasteContent(event: React.MouseEvent) {
|
||||||
|
navigator.clipboard.readText().then((content) => {
|
||||||
|
socket?.emit("input", content);
|
||||||
|
});
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div ref={terminalObj} onContextMenu={pasteContent} style={{ height: "100vh", width: "100%" }}>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -23,11 +23,12 @@ import ClassID from '../TeacherPages/classlistpages/classId';
|
||||||
import ManageTest from '../TeacherPages/TestManage/ManageTest';
|
import ManageTest from '../TeacherPages/TestManage/ManageTest';
|
||||||
import SendTest from '../TeacherPages/TestManage/SendTest';
|
import SendTest from '../TeacherPages/TestManage/SendTest';
|
||||||
import Marking from '../TeacherPages/MarkingPages/Marking';
|
import Marking from '../TeacherPages/MarkingPages/Marking';
|
||||||
import SendTrain from '../TeacherPages/Trainmanage/SendTrain'
|
|
||||||
import TrainManage from '../TeacherPages/Trainmanage/Trainmanage'
|
|
||||||
import StudentLink from '../TeacherPages/MarkingPages/StudentLink';
|
import StudentLink from '../TeacherPages/MarkingPages/StudentLink';
|
||||||
|
|
||||||
|
|
||||||
|
import SendTrain from '../TeacherPages/SendTrain/SendTrain';
|
||||||
|
import TrainManage from '../TeacherPages/SendTest/trainmanage';
|
||||||
|
import Test from "../TeacherPages/teachermanagetest/Test.tsx"
|
||||||
// 定义一个组件来包裹除了特定页面(exam)外的所有页面使其有导航
|
// 定义一个组件来包裹除了特定页面(exam)外的所有页面使其有导航
|
||||||
const MainLayout = ({ headerNav:HeaderNav }) => {
|
const MainLayout = ({ headerNav:HeaderNav }) => {
|
||||||
return (
|
return (
|
||||||
|
@ -69,6 +70,7 @@ function App() {
|
||||||
<Route path="/classID/:key" element={<MainLayout headerNav={HeaderNavTeacher} />}>
|
<Route path="/classID/:key" element={<MainLayout headerNav={HeaderNavTeacher} />}>
|
||||||
<Route index element={<ClassID/>} />
|
<Route index element={<ClassID/>} />
|
||||||
</Route>
|
</Route>
|
||||||
|
<Route path="test" element={<Test />}/>
|
||||||
|
|
||||||
{/* 页面使用单独的布局,不包含HeaderNav和HeaderNavTeacher */}
|
{/* 页面使用单独的布局,不包含HeaderNav和HeaderNavTeacher */}
|
||||||
<Route path="exam/:examId" element={<Exam />} />{/* 练习-考试页面 */}
|
<Route path="exam/:examId" element={<Exam />} />{/* 练习-考试页面 */}
|
||||||
|
|
|
@ -4,7 +4,7 @@ module.exports = function(app) {
|
||||||
app.use(
|
app.use(
|
||||||
'/api', // 如果请求路径匹配'/api',则进行代理
|
'/api', // 如果请求路径匹配'/api',则进行代理
|
||||||
createProxyMiddleware({
|
createProxyMiddleware({
|
||||||
target: 'http://127.0.0.1:5000', // 目标后端服务地址
|
target: 'http://36.138.114.105:32087', // 目标后端服务地址
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
14
build.sh
14
build.sh
|
@ -20,13 +20,14 @@ build_image() {
|
||||||
|
|
||||||
# 推送镜像
|
# 推送镜像
|
||||||
push_image() {
|
push_image() {
|
||||||
|
local service_name=$1
|
||||||
read -p "是否要推送到《达梦启元云原生大数据平台》? [Y/N] " confirm
|
read -p "是否要推送到《达梦启元云原生大数据平台》? [Y/N] " confirm
|
||||||
if [[ $confirm =~ ^[yY]([eE][sS])?$ ]]; then
|
if [[ $confirm =~ ^[yY]([eE][sS])?$ ]]; then
|
||||||
echo "tag"
|
echo "${service_name} tag"
|
||||||
docker tag "${current_dir}-flask-app" 36.138.114.105:31000/cnsof50011836/flask-app
|
docker tag "${service_name}" 36.138.114.105:31000/cnsof50011836/$service_name
|
||||||
echo "pushing"
|
echo "${service_name} pushing"
|
||||||
docker push 36.138.114.105:31000/cnsof50011836/flask-app
|
docker push 36.138.114.105:31000/cnsof50011836/$service_name
|
||||||
echo "pushed"
|
echo "${service_name} pushed"
|
||||||
else
|
else
|
||||||
echo "用户选择不推送镜像"
|
echo "用户选择不推送镜像"
|
||||||
fi
|
fi
|
||||||
|
@ -43,7 +44,8 @@ main() {
|
||||||
build_image build-frontend
|
build_image build-frontend
|
||||||
build_image flask-app
|
build_image flask-app
|
||||||
|
|
||||||
push_image
|
push_image flask-app
|
||||||
|
push_image base-dm
|
||||||
|
|
||||||
echo "脚本结束"
|
echo "脚本结束"
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,14 +23,11 @@ services:
|
||||||
flask-app:
|
flask-app:
|
||||||
build: ./python
|
build: ./python
|
||||||
restart: always
|
restart: always
|
||||||
|
image: flask-app
|
||||||
depends_on:
|
depends_on:
|
||||||
- base-dm
|
- base-dm
|
||||||
ports:
|
ports:
|
||||||
- '8000:8000'
|
- '8000:8000'
|
||||||
healthcheck:
|
- '8765:8765'
|
||||||
test: ["CMD-SHELL", "curl --silent --fail localhost:8000/flask-health-check || exit 1"]
|
|
||||||
interval: 10s
|
|
||||||
timeout: 10s
|
|
||||||
retries: 3
|
|
||||||
command: gunicorn -w 3 -t 60 -b 0.0.0.0:8000 app:app
|
command: gunicorn -w 3 -t 60 -b 0.0.0.0:8000 app:app
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,8 @@ RUN useradd -m -s /bin/bash dmdba \
|
||||||
WORKDIR /home/dmdba
|
WORKDIR /home/dmdba
|
||||||
|
|
||||||
# 传入安装包并解压
|
# 传入安装包并解压
|
||||||
RUN wget https://download.dameng.com/eco/adapter/DM8/202405/dm8_20240408_x86_rh7_64_ent_8.1.3.140.zip
|
RUN wget https://download.dameng.com/eco/adapter/DM8/202405/dm8_20240408_x86_rh7_64_ent_8.1.3.140.zip \
|
||||||
RUN unzip dm8_20240408_x86_rh7_64_ent_8.1.3.140.zip \
|
&& unzip dm8_20240408_x86_rh7_64_ent_8.1.3.140.zip \
|
||||||
&& chown -R dmdba:dmdba /home/dmdba \
|
&& chown -R dmdba:dmdba /home/dmdba \
|
||||||
&& rm -f dm8_20240408_x86_rh7_64_ent_8.1.3.140.zip \
|
&& rm -f dm8_20240408_x86_rh7_64_ent_8.1.3.140.zip \
|
||||||
&& 7z x dm8_20240408_x86_rh7_64.iso \
|
&& 7z x dm8_20240408_x86_rh7_64.iso \
|
||||||
|
|
|
@ -9,6 +9,8 @@ RUN sed -i 's@//.*archive.ubuntu.com@//mirrors.ustc.edu.cn@g' /etc/apt/sources.l
|
||||||
&& apt-get install -y python3.10-venv libssl-dev sudo vim python3-pip ssh wget unzip p7zip* language-pack-zh-hans language-selector-common locales locales-all \
|
&& apt-get install -y python3.10-venv libssl-dev sudo vim python3-pip ssh wget unzip p7zip* language-pack-zh-hans language-selector-common locales locales-all \
|
||||||
# 设置中文环境
|
# 设置中文环境
|
||||||
&& apt install -y $(check-language-support) \
|
&& apt install -y $(check-language-support) \
|
||||||
|
&& sed -i 's/^#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config \
|
||||||
|
&& sudo sed -i 's/^UsePAM yes/UsePAM no/' /etc/ssh/sshd_config \
|
||||||
&& echo "zh_CN.UTF-8 UTF-8" >> /etc/locale.gen \
|
&& echo "zh_CN.UTF-8 UTF-8" >> /etc/locale.gen \
|
||||||
&& sudo /usr/sbin/update-locale LANG=zh_CN.UTF-8 LC_ALL=zh_CN.UTF-8 \
|
&& sudo /usr/sbin/update-locale LANG=zh_CN.UTF-8 LC_ALL=zh_CN.UTF-8 \
|
||||||
&& locale-gen \
|
&& locale-gen \
|
||||||
|
|
|
@ -1,26 +1 @@
|
||||||
FROM ubuntu
|
FROM base-dm
|
||||||
ENV DEBIAN_FRONTEND=noninteractive
|
|
||||||
ENV LANG=zh_CN.UTF-8
|
|
||||||
ENV LC_ALL=zh_CN.UTF-8
|
|
||||||
# 基础软件安装
|
|
||||||
RUN sed -i 's@//.*archive.ubuntu.com@//mirrors.ustc.edu.cn@g' /etc/apt/sources.list \
|
|
||||||
&& apt-get -y update && apt-get -y upgrade \
|
|
||||||
&& apt-get install -y sudo vim python3-pip ssh wget unzip p7zip* language-pack-zh-hans language-selector-common locales locales-all \
|
|
||||||
# 设置中文环境
|
|
||||||
&& apt install -y $(check-language-support) \
|
|
||||||
&& echo "zh_CN.UTF-8 UTF-8" >> /etc/locale.gen \
|
|
||||||
&& sudo /usr/sbin/update-locale LANG=zh_CN.UTF-8 LC_ALL=zh_CN.UTF-8 \
|
|
||||||
&& locale-gen \
|
|
||||||
# 添加用户
|
|
||||||
&& useradd -m -s /bin/bash dmdba \
|
|
||||||
# 修改用户密码
|
|
||||||
&& echo "dmdba:123456" | chpasswd
|
|
||||||
# 传入安装包并解压
|
|
||||||
WORKDIR /home/dmdba
|
|
||||||
RUN wget https://download.dameng.com/eco/adapter/DM8/202405/dm8_20240408_x86_rh7_64_ent_8.1.3.140.zip
|
|
||||||
RUN unzip dm8_20240408_x86_rh7_64_ent_8.1.3.140.zip \
|
|
||||||
&& chown -R dmdba:dmdba /home/dmdba \
|
|
||||||
&& rm -f dm8_20240408_x86_rh7_64_ent_8.1.3.140.zip \
|
|
||||||
&& 7z x dm8_20240408_x86_rh7_64.iso \
|
|
||||||
&& rm -f dm8_20240408_x86_rh7_64.iso \
|
|
||||||
&& chmod +x DMInstall.bin
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
SHELL=/bin/bash
|
||||||
|
KUBERNETES_SERVICE_PORT_HTTPS=443
|
||||||
|
KUBERNETES_SERVICE_PORT=443
|
||||||
|
HOSTNAME=flask-app-deployment-76d66d98b8-s6hxs
|
||||||
|
DMCCA_CANVAS_NAME=flask-app
|
||||||
|
PWD=/home/flask
|
||||||
|
LOGNAME=flask
|
||||||
|
TZ=Asia/Shanghai
|
||||||
|
HOME=/home/flask
|
||||||
|
LANG=zh_CN.UTF-8
|
||||||
|
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
|
||||||
|
VIRTUAL_ENV=/home/flask/venv
|
||||||
|
TERM=xterm
|
||||||
|
USER=flask
|
||||||
|
DMCCA_CANVAS_NAMESPACE=cnsof50011836-system
|
||||||
|
SHLVL=2
|
||||||
|
KUBERNETES_PORT_443_TCP_PROTO=tcp
|
||||||
|
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
|
||||||
|
KUBERNETES_SERVICE_HOST=10.96.0.1
|
||||||
|
LC_ALL=zh_CN.UTF-8
|
||||||
|
KUBERNETES_PORT=tcp://10.96.0.1:443
|
||||||
|
KUBERNETES_PORT_443_TCP_PORT=443
|
||||||
|
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
|
||||||
|
MAIL=/var/mail/flask
|
||||||
|
DEBIAN_FRONTEND=noninteractive
|
||||||
|
OLDPWD=/root
|
||||||
|
_=/usr/bin/printenv
|
|
@ -0,0 +1,3 @@
|
||||||
|
.idea
|
||||||
|
__pycache__
|
||||||
|
public
|
|
@ -4,10 +4,12 @@ FROM base
|
||||||
RUN pip install --upgrade pip
|
RUN pip install --upgrade pip
|
||||||
|
|
||||||
# 创建一个用户运行flask
|
# 创建一个用户运行flask
|
||||||
RUN adduser flask && \
|
RUN useradd -m -s /bin/bash flask && \
|
||||||
chown -R flask:flask /home/flask && \
|
chown -R flask:flask /home/flask && \
|
||||||
mkdir -p /var/log/flask-app && touch /var/log/flask-app/flask-app.err.log && touch /var/log/flask-app/flask-app.out.log && \
|
mkdir -p /var/log/flask-app && touch /var/log/flask-app/flask-app.err.log && touch /var/log/flask-app/flask-app.out.log && \
|
||||||
chown -R flask:flask /var/log/flask-app
|
chown -R flask:flask /var/log/flask-app && \
|
||||||
|
service ssh start && \
|
||||||
|
service ssh restart
|
||||||
WORKDIR /home/flask
|
WORKDIR /home/flask
|
||||||
USER flask
|
USER flask
|
||||||
|
|
||||||
|
@ -24,10 +26,12 @@ RUN python3 -m venv "$VIRTUAL_ENV" && \
|
||||||
export FLASK_APP=app.py && \
|
export FLASK_APP=app.py && \
|
||||||
pip3 install dmPython-2.5.5-cp310-cp310-linux_x86_64.whl && rm dmPython-2.5.5-cp310-cp310-linux_x86_64.whl && \
|
pip3 install dmPython-2.5.5-cp310-cp310-linux_x86_64.whl && rm dmPython-2.5.5-cp310-cp310-linux_x86_64.whl && \
|
||||||
MAKEFLAGS="-j$(nproc)" pip install -r requirements.txt
|
MAKEFLAGS="-j$(nproc)" pip install -r requirements.txt
|
||||||
|
|
||||||
COPY --from=build-dmpython /home/dmdba/build_artifacts/libdmdpi.so /usr/lib/
|
COPY --from=build-dmpython /home/dmdba/build_artifacts/libdmdpi.so /usr/lib/
|
||||||
COPY --from=build-frontend /usr/src/app/build/ /home/flask/public/
|
COPY --from=build-frontend /usr/src/app/build/ /home/flask/public/
|
||||||
|
|
||||||
# 暴露端口
|
# 暴露端口
|
||||||
EXPOSE 5000
|
EXPOSE 8000 22 8765
|
||||||
|
USER root
|
||||||
|
|
||||||
CMD ["gunicorn", "-w", "3", "-t", "60", "-b", "0.0.0.0:8000", "app:app"]
|
CMD ["gunicorn", "-w", "3", "-t", "60", "-b", "0.0.0.0:8000", "app:app"]
|
|
@ -1,12 +1,29 @@
|
||||||
|
import subprocess
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
|
||||||
|
import paramiko
|
||||||
|
|
||||||
from teacher_func import *
|
from teacher_func import *
|
||||||
from student_func import *
|
from student_func import *
|
||||||
|
from k8s_func import *
|
||||||
|
|
||||||
from flask import Flask, render_template, request, jsonify, send_from_directory, session
|
from flask import Flask, render_template, request, jsonify, send_from_directory, session
|
||||||
from flask_cors import CORS
|
from flask_cors import CORS
|
||||||
|
from flask_socketio import SocketIO, emit, disconnect
|
||||||
|
import logging
|
||||||
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
app = Flask(__name__, static_folder="public")
|
app = Flask(__name__, static_folder="public")
|
||||||
CORS(app, resources={r"/*": {"origins": "*"}})
|
CORS(app)
|
||||||
|
cors = CORS(app, resource={
|
||||||
|
r"/*":{
|
||||||
|
"origins":"*"
|
||||||
|
}
|
||||||
|
})
|
||||||
app.config['SECRET_KEY'] = '350625'
|
app.config['SECRET_KEY'] = '350625'
|
||||||
|
socketio = SocketIO(app, cors_allowed_origins="*")
|
||||||
|
|
||||||
|
clients = {}
|
||||||
|
|
||||||
@app.route('/api/student', methods=['POST']) # 检测历史是否登录过,登录则直接进入index,反之则进去login界面
|
@app.route('/api/student', methods=['POST']) # 检测历史是否登录过,登录则直接进入index,反之则进去login界面
|
||||||
def student(): # 判断是否已经登录
|
def student(): # 判断是否已经登录
|
||||||
|
@ -249,6 +266,17 @@ def Find_details():
|
||||||
result=Find_details_Func(ID)
|
result=Find_details_Func(ID)
|
||||||
print(result)
|
print(result)
|
||||||
return jsonify({'TestScore':result})
|
return jsonify({'TestScore':result})
|
||||||
|
@app.route("/api/teacher/list_pods")
|
||||||
|
def teacher_list_pods():
|
||||||
|
return list_pods()
|
||||||
|
@app.route("/api/teacher/list_services")
|
||||||
|
def teacher_list_services():
|
||||||
|
return list_services()
|
||||||
|
|
||||||
|
@app.route("/api/teacher/create_pod")
|
||||||
|
def teacher_create_pod():
|
||||||
|
create_pod(0, "test")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
|
@ -257,7 +285,56 @@ def catch_all(path = "index.html"):
|
||||||
return send_from_directory("public", path)
|
return send_from_directory("public", path)
|
||||||
|
|
||||||
|
|
||||||
|
@socketio.on('connect_ssh')
|
||||||
|
def handle_connect_ssh(data):
|
||||||
|
ip = data['ip']
|
||||||
|
port = int(data['port'])
|
||||||
|
password = data['password']
|
||||||
|
client_id = request.sid
|
||||||
|
|
||||||
|
ssh_client = paramiko.SSHClient()
|
||||||
|
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
|
|
||||||
|
try:
|
||||||
|
ssh_client.connect(ip, port=port, username='your-username', password=password)
|
||||||
|
transport = ssh_client.get_transport()
|
||||||
|
channel = transport.open_session()
|
||||||
|
channel.get_pty()
|
||||||
|
channel.invoke_shell()
|
||||||
|
|
||||||
|
clients[client_id] = {
|
||||||
|
'ssh_client': ssh_client,
|
||||||
|
'channel': channel
|
||||||
|
}
|
||||||
|
|
||||||
|
socketio.start_background_task(target=read_from_channel, channel=channel, client_id=client_id)
|
||||||
|
except Exception as e:
|
||||||
|
emit('output', f'Connection failed: {str(e)}')
|
||||||
|
|
||||||
|
|
||||||
|
def read_from_channel(channel, client_id):
|
||||||
|
while True:
|
||||||
|
if channel.recv_ready():
|
||||||
|
data = channel.recv(1024).decode('utf-8')
|
||||||
|
socketio.emit('output', data, room=client_id)
|
||||||
|
|
||||||
|
|
||||||
|
@socketio.on('input')
|
||||||
|
def handle_input(data):
|
||||||
|
client_id = request.sid
|
||||||
|
if client_id in clients:
|
||||||
|
clients[client_id]['channel'].send(data)
|
||||||
|
|
||||||
|
|
||||||
|
@socketio.on('disconnect')
|
||||||
|
def handle_disconnect():
|
||||||
|
client_id = request.sid
|
||||||
|
if client_id in clients:
|
||||||
|
clients[client_id]['channel'].close()
|
||||||
|
clients[client_id]['ssh_client'].close()
|
||||||
|
del clients[client_id]
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
app.run(host='0.0.0.0', port=5000, debug=True)
|
socketio.run(app, host='0.0.0.0', port=5000)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: NAME
|
||||||
|
namespace: ""
|
||||||
|
labels:
|
||||||
|
app: NAME
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: base-dm
|
||||||
|
image: harbor.dameng.io/cnsof50011836/base-dm
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpu: '1'
|
||||||
|
memory: 1Gi
|
||||||
|
requests:
|
||||||
|
cpu: '0.25'
|
||||||
|
memory: 0.5G
|
|
@ -0,0 +1,85 @@
|
||||||
|
import json
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
from kubernetes import client, config
|
||||||
|
from flask import jsonify
|
||||||
|
# Load in-cluster config
|
||||||
|
config.load_incluster_config()
|
||||||
|
v1 = client.CoreV1Api()
|
||||||
|
namespace = open("/var/run/secrets/kubernetes.io/serviceaccount/namespace").read()
|
||||||
|
IP = "36.138.114.105"
|
||||||
|
def list_pods():
|
||||||
|
ret = v1.list_namespaced_pod(namespace, watch=False)
|
||||||
|
return {
|
||||||
|
"list": [{
|
||||||
|
"name": item.metadata.name.replace("database-dmserver-0", "student-2210502337-0-12"),
|
||||||
|
"ip": item.status.pod_ip
|
||||||
|
} for item in ret.items]
|
||||||
|
}
|
||||||
|
|
||||||
|
def list_services():
|
||||||
|
ret = v1.list_namespaced_service(namespace)
|
||||||
|
return {
|
||||||
|
"list": [
|
||||||
|
# "name": item.metadata.name.replace("database-dmserver-0", "student-2210502337-0-12"),
|
||||||
|
# "ip": item.spec.cluster_ip,
|
||||||
|
# "ports": [[port.to_dict() for port in item.spec.ports]],
|
||||||
|
#
|
||||||
|
item.to_dict()
|
||||||
|
# "node_port": item.spec.ports[0].node_port,
|
||||||
|
for item in ret.items]
|
||||||
|
}
|
||||||
|
|
||||||
|
def create_pod(type, name):
|
||||||
|
# 读取 pod yaml 文件
|
||||||
|
with open(f"assets/type{type}-pod.yaml") as f:
|
||||||
|
pod_manifest = yaml.safe_load(f)
|
||||||
|
pod_manifest["metadata"]["name"] = name
|
||||||
|
pod_manifest["metadata"]["labels"]["app"] = name
|
||||||
|
# 创建 Pod
|
||||||
|
resp = v1.create_namespaced_pod(
|
||||||
|
body=pod_manifest,
|
||||||
|
namespace=namespace
|
||||||
|
)
|
||||||
|
print(f"pod '{resp.metadata.name}' 创建成功")
|
||||||
|
# 创建 Service
|
||||||
|
service_manifest = {
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "Service",
|
||||||
|
"metadata": {
|
||||||
|
"name": name+ "-service"
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"selector": {
|
||||||
|
"app": pod_manifest['metadata']['labels']['app']
|
||||||
|
},
|
||||||
|
"ports": [
|
||||||
|
{
|
||||||
|
"protocol": "TCP",
|
||||||
|
"port": 22,
|
||||||
|
"targetPort": 22
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"protocol": "TCP",
|
||||||
|
"port": 5236,
|
||||||
|
"targetPort": 5236
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resp = v1.create_namespaced_service(
|
||||||
|
body=service_manifest,
|
||||||
|
namespace=namespace
|
||||||
|
)
|
||||||
|
print(f"服务 '{resp.metadata.name}' 创建成功")
|
||||||
|
# 创建成功,再次查询,拿到端口
|
||||||
|
service = v1.read_namespaced_service(name=name+ "-service", namespace=namespace)
|
||||||
|
print(service)
|
||||||
|
print("访问地址:" + IP + ":" + service.spec.ports[0].node_port)
|
||||||
|
|
||||||
|
|
||||||
|
def delete_pod(name):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
Flask==2.3.0
|
Flask==2.3.0
|
||||||
Flask_Cors==4.0.0
|
Flask_Cors==4.0.0
|
||||||
pycryptodome==3.19.0
|
pycryptodome==3.19.0
|
||||||
gunicorn
|
gunicorn
|
||||||
|
kubernetes
|
||||||
|
websockets
|
||||||
|
paramiko
|
||||||
|
flask-socketio
|
|
@ -117,7 +117,7 @@ def get_lesson_func(id):
|
||||||
|
|
||||||
def student_succeed_func(ID):
|
def student_succeed_func(ID):
|
||||||
cursor = db.cursor()
|
cursor = db.cursor()
|
||||||
cursor.execute(f'SELECT * FROM STUDENT WHERE ID={ID}')
|
cursor.execute(f'SELECT * FROM STUDENT WHERE ID={ID}')# 报错
|
||||||
student_succeed = cursor.fetchall()[0]
|
student_succeed = cursor.fetchall()[0]
|
||||||
student_succeeds = student_succeed[0], student_succeed[1], student_succeed[3], student_succeed[4]
|
student_succeeds = student_succeed[0], student_succeed[1], student_succeed[3], student_succeed[4]
|
||||||
cursor.close()
|
cursor.close()
|
||||||
|
|
Loading…
Reference in New Issue