From 96475ab739ab748c8bef662fa7b6eb34c56ed9d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E5=BF=83=E5=AE=87?= Date: Fri, 5 Jan 2024 15:01:16 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9A=82=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Routes.js | 18 +- config/webpack.config.prod.js | 8 +- config/webpack.server.js | 4 +- package-lock.json | 68 ++++++- package.json | 6 +- server/index.js | 18 +- server/render.js | 85 ++++++-- server/service.js | 33 +++ server/window.js | 20 +- src/App.js | 14 +- src/AppConfig.js | 22 +- src/common/UrlTool.js | 10 +- src/components/render-html.jsx | 2 + src/forge/Main/CoderDepot.jsx | 89 +++++--- src/forge/Main/Detail.js | 290 ++++++++++++++++++--------- src/forge/Main/IndexItem.js | 4 +- src/forge/Main/projecthome/Index.jsx | 2 +- src/forge/Team/Index.jsx | 3 + src/forge/users/Index.jsx | 88 ++++++-- src/index.js | 13 +- src/modules/login/LoginDialog.js | 2 +- src/modules/tpm/TPMIndexHOC.js | 7 +- src/redux/actions/actionTypes.js | 8 +- src/redux/actions/index.js | 9 +- src/redux/actions/server.js | 70 +++++++ src/redux/reducers/index.js | 4 +- src/redux/reducers/projectReducer.js | 48 +++++ src/redux/stores/configureStore.js | 6 + src/services/project.js | 30 +++ 29 files changed, 769 insertions(+), 212 deletions(-) create mode 100644 server/service.js create mode 100644 src/redux/actions/server.js create mode 100644 src/redux/reducers/projectReducer.js create mode 100644 src/services/project.js diff --git a/Routes.js b/Routes.js index 906386057..6c4e3099b 100644 --- a/Routes.js +++ b/Routes.js @@ -1,11 +1,19 @@ -import Detail from "./src/forge/Main/DetailAdaptor"; - +import app from './src/App' +import users from './src/forge/users/Index'; +import Detail from "./src/forge/Main/Detail"; export default [ { key:"detail", path: "/:owner/:projectsId", - exact: true, + component: app, + } +]; + +export const deepRoutes = [ + { + key:"detail", + path: "/:owner/:projectsId", component: Detail, - }, -]; \ No newline at end of file + } +] \ No newline at end of file diff --git a/config/webpack.config.prod.js b/config/webpack.config.prod.js index 62e5e54b9..9bd696472 100644 --- a/config/webpack.config.prod.js +++ b/config/webpack.config.prod.js @@ -1,6 +1,7 @@ "use strict"; const autoprefixer = require("autoprefixer"); const path = require("path"); +import { ReactLoadablePlugin } from 'react-loadable/webpack'; const webpack = require("webpack"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); @@ -29,9 +30,9 @@ const terserPlugin = new TerserWebpackPlugin({ terserOptions: { compress: { warnings: false, - drop_console: true, + drop_console: false, drop_debugger: true, - pure_funcs: ['console.log'] //移除console + // pure_funcs: ['console.log'] //移除console } } }); @@ -117,6 +118,9 @@ module.exports = { // please link the files into your node_modules/ and let module-resolution kick in. // Make sure your source files are compiled, as they will not be processed in any way. new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]), + new ReactLoadablePlugin({ + filename: './react-loadable.json', + }), // ["transform-remove-console"] ], }, diff --git a/config/webpack.server.js b/config/webpack.server.js index 403d42d93..c3eae3c77 100644 --- a/config/webpack.server.js +++ b/config/webpack.server.js @@ -32,7 +32,7 @@ const serverConfig = { externals: [ nodeExternals(), // 忽略 Node.js 核心模块 // 自定义的外部模块 - { 'alex': 'Alex', "react": "React", "react-dom": "ReactDOM" } + { "react": "React", "react-dom": "ReactDOM" } ], resolve: { // This allows you to set a fallback for where Webpack should look for modules. @@ -101,7 +101,7 @@ const serverConfig = { { test: /\.(js|jsx|mjs)$/, include: [paths.appSrc, paths.serverSrc], - exclude: /node_modules/, + // exclude: /node_modules/, loader: "babel-loader", options: { cacheDirectory: true, diff --git a/package-lock.json b/package-lock.json index dc34e3114..f6e1726d2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7369,6 +7369,58 @@ "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", "dev": true }, + "connect-timeout": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/connect-timeout/-/connect-timeout-1.9.0.tgz", + "integrity": "sha512-q4bsBIPd+eSGtnh/u6EBOKfuG+4YvwsN0idlOsg6KAw71Qpi0DCf2eCc/Va63QU9qdOeYC8katxoC+rHMNygZg==", + "requires": { + "http-errors": "~1.6.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "on-headers": "~1.0.1" + }, + "dependencies": { + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==" + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmmirror.com/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "requires": { + "ee-first": "1.1.1" + } + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmmirror.com/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==" + } + } + }, "connected-react-router": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/connected-react-router/-/connected-react-router-4.4.1.tgz", @@ -12148,7 +12200,7 @@ }, "http-proxy": { "version": "1.18.1", - "resolved": "https://registry.npmmirror.com/http-proxy/-/http-proxy-1.18.1.tgz", + "resolved": "http://173.15.15.82:8081/repository/npm-all/http-proxy/-/http-proxy-1.18.1.tgz", "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", "requires": { "eventemitter3": "^4.0.0", @@ -12158,7 +12210,7 @@ "dependencies": { "eventemitter3": { "version": "4.0.7", - "resolved": "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-4.0.7.tgz", + "resolved": "http://173.15.15.82:8081/repository/npm-all/eventemitter3/-/eventemitter3-4.0.7.tgz", "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" } } @@ -12218,12 +12270,12 @@ }, "is-extglob": { "version": "2.1.1", - "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" }, "is-glob": { "version": "4.0.3", - "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "requires": { "is-extglob": "^2.1.1" @@ -13778,6 +13830,11 @@ } } }, + "jquery": { + "version": "3.7.1", + "resolved": "https://registry.npmmirror.com/jquery/-/jquery-3.7.1.tgz", + "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==" + }, "js-base64": { "version": "2.6.4", "resolved": "http://173.15.15.82:8081/repository/npm-all/js-base64/-/js-base64-2.6.4.tgz", @@ -15635,8 +15692,7 @@ "on-headers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "dev": true + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" }, "once": { "version": "1.4.0", diff --git a/package.json b/package.json index 902137b9f..8b9a2ab15 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "clipboard": "^2.0.8", "code-prettify": "^0.1.0", "codemirror": "^5.64.0", + "connect-timeout": "^1.9.0", "connected-react-router": "4.4.1", "core-js": "^3.34.0", "dompurify": "^2.3.3", @@ -60,6 +61,7 @@ "intersection-observer": "^0.12.2", "isomorphic-style-loader": "^5.3.2", "jest": "20.0.4", + "jquery": "^3.7.1", "js-base64": "^2.5.2", "js2wordcloud": "^1.1.12", "jsdom": "^15.2.1", @@ -141,7 +143,7 @@ "xterm-addon-fit": "0.4.0" }, "scripts": { - "start": "node --max_old_space_size=15360 scripts/start.js", + "start": "node scripts/start.js", "build": "cross-env NODE_ENV=production babel-node --max_old_space_size=15360 scripts/build.js", "build:dll": "webpack --config=./config/webpack.dll.config.js", "test-build": "cross-env NODE_ENV=testBuild node --max_old_space_size=15360 scripts/build.js", @@ -152,7 +154,7 @@ "analyz": "NODE_ENV=production npm_config_report=true npm run build", "dev": "npm-run-all --parallel dev:**", "dev:server": "cross-env NODE_ENV=production nodemon --exec babel-node \"./buildserver/bundle.js\" --watch config --watch server", - "dev:build:server": "cross-env NODE_ENV=production webpack --config config/webpack.server.js --watch" + "dev:build:server": "cross-env NODE_ENV=production webpack --config config/webpack.server.js" }, "jest": { "collectCoverageFrom": [ diff --git a/server/index.js b/server/index.js index e9107d493..9d51d9cc2 100644 --- a/server/index.js +++ b/server/index.js @@ -11,15 +11,31 @@ const targetServer = 'https://www.gitlink.org.cn'; // Java 服务器的地址 const options = { target: targetServer, changeOrigin: true, // 允许在请求头中更改主机 + timeout: 30000 }; // 设置代理 -app.use('/api', (req, res) => { +app.use(['/api', '/images', '/system', '/favicon'], (req, res) => { const proxy = createProxyMiddleware(options); proxy(req, res); }); +// 中间件,用于排除特定的路径 +function excludePath(req, res, next) { + // 检查请求的路径是否需要被排除 + if (req.path.includes('/build/')) { + // 如果是,直接结束请求,不调用下一个中间件或路由处理器 + res.status(404).send('This path is excluded.'); + } else { + // 如果不是,继续执行下一个中间件或路由处理器 + next(); + } +} +// 应用中间件到所有路由 +app.use(excludePath); + app.get('*',function (req,res) { + console.log('render', req.url) render(req,res); }) diff --git a/server/render.js b/server/render.js index 2ba3bde06..cc2b16d90 100644 --- a/server/render.js +++ b/server/render.js @@ -1,24 +1,85 @@ import React from "react"; import {renderToString} from "react-dom/server"; -import {StaticRouter} from "react-router-dom"; -import {renderRoutes} from "react-router-config"; -import Routes from "../Routes"; -import { Provider } from "react-redux"; -import fs from 'fs' -import path from 'path'; +import {StaticRouter, matchPath} from "react-router-dom"; -export const render = (req,res)=>{ - const context = { - css: [] +import {renderRoutes} from "react-router-config"; +import Routes, { deepRoutes } from "../Routes"; +import { Provider } from "react-redux"; +import { getPathType } from './service' +import { serverStore } from "../src/redux/stores/configureStore"; +import App from "../src/App"; +import { Route } from 'react-router-dom'; + +export const render = async (req,res)=>{ + let _route = null, + _match = null; + + const pathData = await getPathType('Eeeros') + const store = serverStore(); + + + function matchSubRoutes(routes, url) { + for (const route of routes) { + // 检查当前路由是否匹配 + let match = matchPath(url, route.path); + if (match) { + // 如果当前路由匹配,并且它有子路由,递归检查子路由 + if (route.routes) { + // 递归调用匹配子路由 + const subMatch = matchSubRoutes(route.routes, url); + if (subMatch) { + _route = route; + _match = subMatch; + return true; + } + } + // 如果没有子路由,或者子路由不匹配,但当前路由匹配,返回当前路由 + _route = route; + _match = match; + return true; + } + } + // 如果没有找到匹配的路由,返回false + return false; + } + + matchSubRoutes(deepRoutes, req.url.split("?")[0]) + + let context = { + code: 200, }; + + if (_route && _route.component && _route.component.preFetch) { + context = await _route.component.preFetch({ + store, + match: _match, + query: req.path, + }); + } + const content = renderToString(( - - {renderRoutes(Routes)} + + + + )) - let html=fs.readFileSync(path.join(path.resolve('./build'),'index.html'),'utf-8'); + + // 注入state + + let script = domObj.window.document.getElementById('initState') + if (!script) { + script = domObj.window.document.createElement('script') + script.setAttribute('id', 'initState') + } + script.textContent = `window.__initState__ = ${ JSON.stringify(store.getState()) }` + + domObj.window.document.head.appendChild(script); + + let html = domObj.serialize() + const prepHTML=(data,rootString)=>{ data=data.replace('
',`
${rootString}
`); diff --git a/server/service.js b/server/service.js new file mode 100644 index 000000000..e6e99eea7 --- /dev/null +++ b/server/service.js @@ -0,0 +1,33 @@ +import axios from "axios"; + +const instance = axios.create({ + baseURL: 'https://www.gitlink.org.cn', // 你的API基础URL + timeout: 20000, // 请求超时时间 + headers: { + 'Content-Type': 'application/json', + }, +}); + +// 封装GET请求 +function get(url, params) { + return instance.get(url, { params }).then(response => response.data); +} + +// 封装POST请求 +function post(url, data) { + return instance.post(url, data).then(response => response.data); +} + +// 封装PUT请求 +function put(url, data) { + return instance.put(url, data).then(response => response.data); +} + +// 封装DELETE请求 +function deleteRequest(url) { + return instance.delete(url).then(response => response.data); +} + +export const getPathType = (pathname) => { + return get(`/api/owners/${pathname}.json`) +} diff --git a/server/window.js b/server/window.js index 54366d84e..cb1e790c9 100644 --- a/server/window.js +++ b/server/window.js @@ -1,14 +1,30 @@ const jsdom = require("jsdom"); import fs from 'fs' import path from 'path'; +import axios from "axios"; const { JSDOM } = jsdom; -const html=fs.readFileSync(path.join(path.resolve('./build'),'index.html'),'utf-8'); - +const html = fs.readFileSync(path.join(path.resolve('./build'),'index.html'),'utf-8'); const dom = new JSDOM(html); const { window } = dom; +const $ = require( "jquery" )( window ) + global.window = window; +global.domObj = dom; global.document = window.document; global.navigator = window.navigator; +global.localStorage = {} + +axios.get('https://gw.alipayobjects.com/os/lib/alipay/alex/2.0.19/bundle/alex.all.global.min.js') + .then(response => { + // 处理响应 + if (response) { + // 这里你可以直接使用response.data,而不是eval + eval(response); + } + }) + .catch(error => { + console.error('Error fetching data:', error); + }); diff --git a/src/App.js b/src/App.js index 1d51f69a9..5e4e4b9e0 100644 --- a/src/App.js +++ b/src/App.js @@ -22,11 +22,9 @@ import SiderBarHelp from './glcc/siderBarHelp'; import { SnackbarHOC } from 'educoder'; import { initAxiosInterceptors } from './AppConfig' -import { Provider } from 'react-redux'; -import configureStore from './redux/stores/configureStore'; import cookie from 'react-cookies'; +import InfosIndex from './forge/users/Index' -const store = configureStore(); window.marked = marked; const theme = createMuiTheme({ palette: { @@ -77,10 +75,10 @@ const http500 = Loadable({ loader: () => import('./modules/500/http500'), loading: Loading, }) -const InfosIndex = Loadable({ - loader: () => import('./forge/users/Index'), - loading: Loading, -}) +// const InfosIndex = Loadable({ +// loader: () => import('./forge/users/Index'), +// loading: Loading, +// }) // 组织 const OrganizeIndex = Loadable({ loader: () => import('./forge/Team/Index'), @@ -328,7 +326,6 @@ class App extends Component { render() { const { pathType, pathName, mygetHelmetapi } = this.state; return ( - this.Modifyloginvalue()}> @@ -536,7 +533,6 @@ class App extends Component { - ); } } diff --git a/src/AppConfig.js b/src/AppConfig.js index 5c5b188a5..f79c518b0 100644 --- a/src/AppConfig.js +++ b/src/AppConfig.js @@ -18,14 +18,14 @@ function locationurl(list) { // TODO 开发期多个身份切换 let debugType = "" if (isDev) { - const _search = window.location.search; - let parsed = {}; - if (_search) { - parsed = queryString.parse(_search); - } - debugType = window.location.search.indexOf('debug=t') !== -1 ? 'teacher' : - window.location.search.indexOf('debug=s') !== -1 ? 'student' : - window.location.search.indexOf('debug=a') !== -1 ? 'admin' : parsed.debug || 'admin' + // const _search = window.location.search; + // let parsed = {}; + // if (_search) { + // parsed = queryString.parse(_search); + // } + // debugType = window.location.search.indexOf('debug=t') !== -1 ? 'teacher' : + // window.location.search.indexOf('debug=s') !== -1 ? 'student' : + // window.location.search.indexOf('debug=a') !== -1 ? 'admin' : parsed.debug || 'admin' } window._debugType = debugType; export function initAxiosInterceptors(props) { @@ -56,6 +56,7 @@ export function initAxiosInterceptors(props) { // } // } config.url = url; + console.log(config.url) config.withCredentials = false; return config; }, @@ -95,13 +96,14 @@ export function initAxiosInterceptors(props) { } if (response.data.status === 404) { - let responseURL = response.request ? response.request.responseURL:''; + let responseURL = (response && response.request) ? response.request.responseURL:''; // 组织和个人的拥有情况,404不跳转 + console.log(responseURL) if (responseURL.indexOf('/api/users/') === -1 && responseURL.indexOf('/api/organizations/') === -1 ) { // 邀请页面不进行404跳转 if( window.location.pathname.includes('/invite') && (responseURL.includes('/simple.json')||responseURL.includes('/detail.json')||responseURL.includes('/menu_list.json'))){ }else{ - locationurl('/nopage'); + // locationurl('/nopage'); } } } diff --git a/src/common/UrlTool.js b/src/common/UrlTool.js index 841525a78..a84a6a9fa 100644 --- a/src/common/UrlTool.js +++ b/src/common/UrlTool.js @@ -348,7 +348,13 @@ export function setSeoMeta(keyWords, title, description, url, owner, projectId) if(owner)keyStatement += owner; if(projectId)keyStatement += `/${projectId}`; + if (domObj) { + console.log('==================domObj===================') + document = domObj.window.document + console.log('==================document===================', document.querySelector(`meta[property='og:title']`).content) + } document.querySelector(`meta[property='og:title']`).content = title + keyStatement + ' for gitlink' + keyStatement + ' for git'; + console.log('==================document===================', document.querySelector(`meta[property='og:title']`).content) document.querySelector(`meta[property='og:url']`).content = window.location.origin + url; document.querySelector(`meta[property='og:description']`).content = description + ' - ' + title + ' for gitlink' + keyStatement + ' for git'; document.querySelector(`meta[property='og:image:alt']`).content = description + ' - ' + title + ' for gitlink' + keyStatement + ' for git'; @@ -364,5 +370,7 @@ export function setSeoMeta(keyWords, title, description, url, owner, projectId) document.querySelector(`meta[name='twitter:description']`).content = description + ' - ' + title + keyStatement + ' for gitlink' + keyStatement + ' for git'; document.querySelector(`link[rel='canonical']`).href = window.location.origin + url; - + if (domObj) { + domObj.window.document.querySelector('meta[name="Keywords"]') + } } \ No newline at end of file diff --git a/src/components/render-html.jsx b/src/components/render-html.jsx index 68c969e2d..89c43ff34 100644 --- a/src/components/render-html.jsx +++ b/src/components/render-html.jsx @@ -56,6 +56,7 @@ export default ({ } let str = String(value); + console.log(str) const html = useMemo(() => { let rs = marked(str); const math_expressions = getMathExpressions(); @@ -104,6 +105,7 @@ export default ({ rs = dompurify.sanitize(rs.replace(/▁/g, "▁▁▁")) rs = rs.replaceAll('{ - if (projectsId && owner && defaultBranch && mirror_status===0 && !search){ - let b = turnbar(branchName) ; + let b = turnbar(branchName) ; + if (projectsId && owner && defaultBranch && mirror_status===0 && !search && pathname.indexOf(`/tree/${b}/`) > -1){ let url = pathname.split(`/tree/${b}/`)[1]; getFileInfo(url,branchName); } @@ -565,18 +572,18 @@ function CoderDepot(props){ }) } { - fileInfo && - + // fileInfo && + // } @@ -671,4 +678,20 @@ function CoderDepot(props){ ) } -export default CoderDepot; \ No newline at end of file + +const mapStateToProps = (state) => { + const { + defaultDetail, + projectEntries, + projectReadMe + } = state.projectReducer; + return { + defaultDetail, + projectEntries, + projectReadMe + }; +} + +export default connect( + mapStateToProps +)(CoderDepot); \ No newline at end of file diff --git a/src/forge/Main/Detail.js b/src/forge/Main/Detail.js index 6a3f4802d..708202895 100644 --- a/src/forge/Main/Detail.js +++ b/src/forge/Main/Detail.js @@ -2,11 +2,14 @@ import React, { Component } from 'react'; import { Spin, Tooltip } from 'antd'; import { Link, Route, Switch } from 'react-router-dom'; import { withRouter } from "react-router"; +import { connect } from 'react-redux'; import { Content, AlignTop } from '../Component/layout'; import DetailBanner from './sub/DetailBanner'; +import CoderDepot from './CoderDepot' import cookie from 'react-cookies'; import { setSeoMeta } from 'educoder'; import { Base64 } from 'js-base64'; +import { setProject, setProjectDetail, setProjectEntries, setProjectMenuList, setProjectReadMe } from '../../redux/actions/server'; import '../css/index.scss' import './list.scss'; @@ -15,6 +18,7 @@ import Loadable from 'react-loadable'; import Loading from '../../Loading'; import axios from 'axios'; +import { async } from 'q'; const Setting = Loadable({ loader: () => import('../Settings/Index'), @@ -88,10 +92,10 @@ const CoderRootCommit = Loadable({ loader: () => import('./CoderRootCommit'), loading: Loading, }) -const CoderDepot = Loadable({ - loader: () => import('./CoderDepot'), - loading: Loading, -}) +// const CoderDepot = Loadable({ +// loader: () => import('./CoderDepot'), +// loading: Loading, +// }) const TrendsIndex = Loadable({ loader: () => import('../Activity/Activity'), @@ -163,11 +167,28 @@ function checkPathname(projectsId, owner, pathname) { } return name; } + +const getProjectFunc = (owner, projectsId, isServer = false) => { + const url = isServer ? `https://www.gitlink.org.cn/api/${owner}/${projectsId}/simple.json` : `/${owner}/${projectsId}/simple.json`; + return axios.get(url) +} + +const getDetailFunc = (owner, projectsId, isServer = false) => { + const url = isServer ? `https://www.gitlink.org.cn/api/${owner}/${projectsId}/detail.json` : `/${owner}/${projectsId}/detail.json`; + return axios.get(url) +} + +const getBannerFunc = (owner, projectsId, isServer = false) => { + const url = isServer ? `https://www.gitlink.org.cn/api/${owner}/${projectsId}/menu_list.json` : `/${owner}/${projectsId}/menu_list.json`; + return axios.get(url) +} + class Detail extends Component { constructor(props) { super(props); + console.log('=================defaultDetail================', props.defaultDetail) this.state = { - projectDetail: undefined, + projectDetail: props.defaultDetail || undefined, isManager: false, isReporter: false, isDeveloper: false, @@ -180,7 +201,7 @@ class Detail extends Component { http_url: undefined, branchs: undefined, branchList: undefined, - project: null, + project: props.projectBase || undefined, firstSync: false, secondSync: false, open_devops: false, @@ -190,22 +211,27 @@ class Detail extends Component { // 非本平台项目 platform: false, - mirror_status:2 + mirror_status:2, + bannerList: props.projectMenu || [] } } componentDidMount = () => { - this.getProject(); + // this.getProject(); let history = this.props.location; this.clearIssueCookies(history); } - componentDidUpdate = (prevState) => { + componentDidUpdate = async (prevState) => { let prevParam = prevState.match.params; let propsParam = this.props.match.params; if (prevState && this.props && (prevParam.projectsId !== propsParam.projectsId || prevParam.owner !== propsParam.owner)) { - this.getProject(); + const { projectsId, owner } = this.props.match.params; + const data = await getProjectFunc(owner, projectsId); + if (data.data) { + this.getProject(data.data) + } } this.props.history.listen((history) => { @@ -223,56 +249,77 @@ class Detail extends Component { cookie.save('issuestates', undefined, { expires: 0, path: `/` }); } } + async componentWillMount() { + const { projectsId, owner } = this.props.match.params; + + if (!this.props.projectBase) { + const data = await getProjectFunc(owner, projectsId); + console.log('======================getProjectFunc==============') + if (data.data) { + this.getProject(data.data) + } + } else { + console.log('======================this.props.projectBase==============') + this.getProject(this.props.projectBase) + } + } componentWillUnmount() { this.timerChannel && clearTimeout(this.timerChannel); } - getProject = (num) => { + getProject = async (data) => { const { projectsId, owner } = this.props.match.params; - const url = `/${owner}/${projectsId}/simple.json`; - axios.get(url).then((result) => { - if (result && result.data) { - this.setState({ - project: result.data, - open_devops: result.data.open_devops, - platform: result.data.platform && result.data.platform !== 'educoder' - }) - if (result.data.type !== 0 && result.data.mirror_status === 1) { - console.log("--------start channel --------"); - // 是镜像项目,且未完成迁移 - this.canvasChannel(); - if (num) { - this.setState({ - secondSync: true, - firstSync: false - }) - } else { - this.setState({ - firstSync: true, - secondSync: false - }) - } - this.setState({ - mirror_status:1 - }) - } else if (result.data.mirror_status === 2) { - this.setState({ - mirror_status:2, - firstSync: false - }) - this.deleteProjectBack(); - } else { - this.setState({ - firstSync: false, - secondSync: false, - mirror_status:0 - }) - this.getBanner(); - this.getDetail(); - } - } + this.setState({ + project: data, + open_devops: data.open_devops, + platform: data.platform && data.platform !== 'educoder' }) + if (data.type !== 0 && data.mirror_status === 1) { + console.log("--------start channel --------"); + // 是镜像项目,且未完成迁移 + this.canvasChannel(); + if (num) { + this.setState({ + secondSync: true, + firstSync: false + }) + } else { + this.setState({ + firstSync: true, + secondSync: false + }) + } + this.setState({ + mirror_status:1 + }) + } else if (data.mirror_status === 2) { + this.setState({ + mirror_status:2, + firstSync: false + }) + this.deleteProjectBack(); + } else { + this.setState({ + firstSync: false, + secondSync: false, + mirror_status:0 + }) + console.log('==================projectMenu=========', this.props.projectMenu) + this.getBanner(this.props.projectMenu) + let projectdata + if (this.props.defaultDetail) { + console.log('========================this.props.defaultDetail=================') + projectdata = this.props.defaultDetail + } else { + console.log('========================getDetailFunc=================') + projectdata = await getDetailFunc(owner, projectsId, !window.location.host).data; + } + if (projectdata) { + console.log('========================projectdata=================') + this.getDetail(projectdata) + } + } } // 工作流激活后修改状态 @@ -356,58 +403,68 @@ class Detail extends Component { }); } - getDetail = () => { + getDetail = async (data) => { const { projectsId, owner } = this.props.match.params; - const url = `/${owner}/${projectsId}/detail.json`; - axios.get(url).then((result) => { - if (result && result.data) { - if (result.data.status === 404) { - if (window.location.pathname.includes('/invite')) { - let inviteString = window.location.search && window.location.search.split('?invite=')[1]; - let data = inviteString && JSON.parse(Base64.decode(inviteString)); - const { project = {}, projectDetail = {} } = this.state - this.setState({ - project: Object.assign(project, { author: { name: data.ownerName } }), - projectDetail: Object.assign(projectDetail, { name: data.projectName }) - }); - } else { - this.props.history.push('/nopage'); - } - } else { - this.setState({ - projectDetail: result.data, - project_id: result.data.project_id, - isManager: result.data.permission && (result.data.permission === "Manager" || result.data.permission === "Admin" || result.data.permission === "Owner"), - isReporter: result.data.permission && result.data.permission === "Reporter", - isDeveloper: result.data.permission && result.data.permission === "Developer", - http_url: result.data.clone_url, - praised: result.data.praised, - watched: result.data.watched, - watchers_count: result.data.watchers_count, - praises_count: result.data.praises_count, - forked_count: result.data.forked_count, - defaultBranch: result.data.default_branch - }); - // seo优化设置 - let keyWords=`${owner},${projectsId},${result.data.author.name},`; - let title= `${owner}/${projectsId}${result.data.description?':'+result.data.description:''}`; - setSeoMeta(keyWords,title,result.data.description,`/${owner}/${projectsId}`,owner,projectsId); - } + if (!data) { + const url = `/${owner}/${projectsId}/detail.json`; + const res = await axios.get(url) + if (res.data) { + data = res.data } - }).catch((error) => { }) + } + console.log('=============data==============') + if (data) { + if (data.status === 404) { + if (window.location.pathname.includes('/invite')) { + let inviteString = window.location.search && window.location.search.split('?invite=')[1]; + let inviteData = inviteString && JSON.parse(Base64.decode(inviteString)); + const { project = {}, projectDetail = {} } = this.state + this.setState({ + project: Object.assign(project, { author: { name: inviteData.ownerName } }), + projectDetail: Object.assign(projectDetail, { name: inviteData.projectName }) + }); + } else { + this.props.history.push('/nopage'); + } + } else { + this.setState({ + projectDetail: data, + project_id: data.project_id, + isManager: data.permission && (data.permission === "Manager" || data.permission === "Admin" || data.permission === "Owner"), + isReporter: data.permission && data.permission === "Reporter", + isDeveloper: data.permission && data.permission === "Developer", + http_url: data.clone_url, + praised: data.praised, + watched: data.watched, + watchers_count: data.watchers_count, + praises_count: data.praises_count, + forked_count: data.forked_count, + defaultBranch: data.default_branch + }); + // seo优化设置 + let keyWords=`${owner},${projectsId},${data.author.name},`; + let title= `${owner}/${projectsId}${data.description?':'+data.description:''}`; + setSeoMeta(keyWords,title,data.description,`/${owner}/${projectsId}`,owner,projectsId); + } + } } // 获取动态导航栏菜单 - getBanner() { + async getBanner(data) { const { projectsId, owner } = this.props.match.params; - const url = `/${owner}/${projectsId}/menu_list.json`; - axios.get(url).then(result => { - if (result) { - this.setState({ - bannerList: result.data - }) + if (!data) { + const url = `/${owner}/${projectsId}/menu_list.json`; + const res = await axios.get(url) + if (res.data) { + data = res.data } - }).catch(error => { }) + } + + if (data) { + this.setState({ + bannerList: data + }) + } } // 关注和取消关注 @@ -877,4 +934,39 @@ class Detail extends Component { } } -export default withRouter(Detail); +const mapStateToProps = (state) => { + const { + projectBase, + defaultDetail, + projectMenu, + } = state.projectReducer; + return { + projectBase, + defaultDetail, + projectMenu, + }; +} + +Detail.preFetch = ({ store, match, query }) => { + return new Promise(async function(resolve, reject) { + let { dispatch, getState } = store; + // const { owner,projectsId } = match.params + console.log(match.params) + const promises = [ + dispatch(setProjectDetail(match.params)), + dispatch(setProject(match.params)), + dispatch(setProjectEntries({...match.params, branch: 'master'})), + dispatch(setProjectMenuList(match.params)), + dispatch(setProjectReadMe({...match.params, branch: 'master'})) + + ]; + const data = await Promise.all(promises); + resolve({ + code: 200, + }); + }); +}; + +export default withRouter(connect( + mapStateToProps +)(Detail)); \ No newline at end of file diff --git a/src/forge/Main/IndexItem.js b/src/forge/Main/IndexItem.js index a591ed0d8..2d037f694 100644 --- a/src/forge/Main/IndexItem.js +++ b/src/forge/Main/IndexItem.js @@ -28,9 +28,9 @@ class IndexItem extends Component { : - + { window.location.href = `/${i.author && i.author.login}/${i.identifier}` }} className="show-user-link"> - + }
diff --git a/src/forge/Main/projecthome/Index.jsx b/src/forge/Main/projecthome/Index.jsx index 1fc10cfa2..503c4cf88 100644 --- a/src/forge/Main/projecthome/Index.jsx +++ b/src/forge/Main/projecthome/Index.jsx @@ -114,7 +114,7 @@ function Index() { }
- {i.author && i.author.name}/{i.name} + { window.location.href = `/${i.author && i.author.login}/${i.identifier}` }} className="infotitle task-hide">{i.author && i.author.name}/{i.name} {i.praises_count > 0 ? {i.praises_count} :"" } {i.forked_count > 0 ? {i.forked_count}:""}
diff --git a/src/forge/Team/Index.jsx b/src/forge/Team/Index.jsx index 81266ca64..7094a2332 100644 --- a/src/forge/Team/Index.jsx +++ b/src/forge/Team/Index.jsx @@ -41,6 +41,9 @@ const ProjectDetail = Loadable({ }); export default withRouter(CNotificationHOC()(SnackbarHOC()(TPMIndexHOC( ((props)=>{ + + console.log(456) + return (
diff --git a/src/forge/users/Index.jsx b/src/forge/users/Index.jsx index 8ed8b1e4e..1e03f51f3 100644 --- a/src/forge/users/Index.jsx +++ b/src/forge/users/Index.jsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { Component } from 'react'; import { Route, Switch } from "react-router-dom"; import Loadable from "react-loadable"; import Loading from "../../Loading"; @@ -6,41 +6,99 @@ import { withRouter } from "react-router"; import { SnackbarHOC } from "educoder"; import { CNotificationHOC } from "../../modules/courses/common/CNotificationHOC"; import { TPMIndexHOC } from "../../modules/tpm/TPMIndexHOC"; +import ProjectDetail from "../Main/Detail"; const Infos = Loadable({ loader: () => import("./Infos"), loading: Loading, }); -// forge项目详情 -const ProjectDetail = Loadable({ - loader: () => import("../Main/Detail"), - loading: Loading, -}); -export default withRouter( - (CNotificationHOC()(SnackbarHOC()(TPMIndexHOC((props) => { +// // forge项目详情 +// const ProjectDetail = Loadable({ +// loader: () => import("../Main/Detail"), +// loading: Loading, +// }); +class DetailTop extends Component { + constructor(props) { + super(props); + this.state = {} + } + + getProject = (num) => { + const { projectsId, owner } = this.props.match.params; + const url = `https://www.gitlink.org.cn/${owner}/${projectsId}/simple.json`; + axios.get(url).then((result) => { + if (result && result.data) { + this.setState({ + project: result.data, + open_devops: result.data.open_devops, + platform: result.data.platform && result.data.platform !== 'educoder' + }) + if (result.data.type !== 0 && result.data.mirror_status === 1) { + console.log("--------start channel --------"); + // 是镜像项目,且未完成迁移 + this.canvasChannel(); + if (num) { + this.setState({ + secondSync: true, + firstSync: false + }) + } else { + this.setState({ + firstSync: true, + secondSync: false + }) + } + this.setState({ + mirror_status:1 + }) + } else if (result.data.mirror_status === 2) { + this.setState({ + mirror_status:2, + firstSync: false + }) + this.deleteProjectBack(); + } else { + this.setState({ + firstSync: false, + secondSync: false, + mirror_status:0 + }) + this.getBanner(); + this.getDetail(); + } + } + }) + } + + render() { // 个人中心新增的二级目录都需在此添加一次。将项目放组织和个人名下后,此处根据二级目录作判断, let secondRouter = ''; - if (props.location.pathname) { - secondRouter = props.location.pathname.split('/')[2]; + if (this.props.location.pathname) { + secondRouter = this.props.location.pathname.split('/')[2]; } let userRouterArr = ['statistics', 'projects', 'notice', 'devops', 'organizes', 'info', 'following', 'followers', 'password' , "general", "blockchain"]; + return ( - {secondRouter && (!userRouterArr.includes(secondRouter)) ? ( - + )} > : ( - + )} >} ) - })))) -) \ No newline at end of file + } +} +const user = (CNotificationHOC()(SnackbarHOC()(TPMIndexHOC(DetailTop)))) + +user.preFetch = ProjectDetail.preFetch + +export default withRouter(user) \ No newline at end of file diff --git a/src/index.js b/src/index.js index d4cd20736..01ee36c19 100644 --- a/src/index.js +++ b/src/index.js @@ -7,20 +7,27 @@ import './index.css'; import App from './App'; import { configureUrlQuery } from 'react-url-query'; import history from './history'; +import configureStore from './redux/stores/configureStore'; +import { Provider } from 'react-redux'; configureUrlQuery({ history }); +const store = configureStore(); window.__useKindEditor = false; const render = (Component) => { - ReactDOM.render( - + ReactDOM.hydrate( + {/* */} + + - + + + , document.getElementById('root') ) diff --git a/src/modules/login/LoginDialog.js b/src/modules/login/LoginDialog.js index 0cdbf6c85..d0a56e056 100644 --- a/src/modules/login/LoginDialog.js +++ b/src/modules/login/LoginDialog.js @@ -331,7 +331,7 @@ class LoginDialog extends Component { this.setState({ isphone: flag, //查询第三方登录信息 - settings: JSON.parse(localStorage.getItem("chromesetting")), + settings: JSON.parse(localStorage.getItem("chromesetting") || '{}'), }) if (this.props.isRender != undefined) { diff --git a/src/modules/tpm/TPMIndexHOC.js b/src/modules/tpm/TPMIndexHOC.js index 51a4b97db..9b9e61de6 100644 --- a/src/modules/tpm/TPMIndexHOC.js +++ b/src/modules/tpm/TPMIndexHOC.js @@ -61,7 +61,8 @@ export function TPMIndexHOC(WrappedComponent) { window.removeEventListener('keyup', this.keyupListener) } componentWillMount() { - this.fetchUsers(); + // console.log(123) + // this.fetchUsers(); } componentDidMount() { @@ -165,7 +166,7 @@ export function TPMIndexHOC(WrappedComponent) { publicNav:false }) } - let url = `/users/get_user_info.json`; + let url = `/api/users/get_user_info.json`; axios.get(url).then((response) => { if (response && response.data) { this.initCommonState(response.data) @@ -317,7 +318,7 @@ export function TPMIndexHOC(WrappedComponent) {
{!publicNav &&
} { - current_user && + // current_user && this.initCommonState(user)} {...this.props} {...this.state} {...common} > diff --git a/src/redux/actions/actionTypes.js b/src/redux/actions/actionTypes.js index c35d054eb..67fa40929 100644 --- a/src/redux/actions/actionTypes.js +++ b/src/redux/actions/actionTypes.js @@ -106,7 +106,13 @@ const types = { IS_SHOW_WXCODE_TEST_CASES: 'IS_SHOW_WXCODE_TEST_CASES', SHOW_WX_CODE_LOADING: 'SHOW_WX_CODE_LOADING', SHOW_WX_CODE_DIALOG: 'SHOW_WX_CODE_DIALOG', - SET_GOLD_AND_EXPERIENCE: 'SET_GOLD_AND_EXPERIENCE' + SET_GOLD_AND_EXPERIENCE: 'SET_GOLD_AND_EXPERIENCE', + /** 服务端渲染 */ + GET_PROJECT_BASE: 'GET_PROJECT_BASE', + GET_PROJECT_DETAIL: 'GET_PROJECT_DETAIL', + GET_PROJECT_ENTRIES: 'GET_PROJECT_ENTRIES', + GET_PROJECT_MENU: 'GET_PROJECT_MENU', + GET_PROJECT_README: 'GET_PROJECT_README', } export default types; diff --git a/src/redux/actions/index.js b/src/redux/actions/index.js index 5f95d2b8b..698e69f96 100644 --- a/src/redux/actions/index.js +++ b/src/redux/actions/index.js @@ -126,6 +126,11 @@ import { changeWXCodeEvaluateLoading, changeWXCodeEvaluateDialog } from './wxCode'; + +import { + setProject, +} from './server.js'; + export default { toggleTodo, getOJList, @@ -223,5 +228,7 @@ export default { evaluateWxCode, showWXCodeTextCase, changeWXCodeEvaluateLoading, - changeWXCodeEvaluateDialog + changeWXCodeEvaluateDialog, + // 服务端渲染 + setProject } \ No newline at end of file diff --git a/src/redux/actions/server.js b/src/redux/actions/server.js new file mode 100644 index 000000000..9439b4a22 --- /dev/null +++ b/src/redux/actions/server.js @@ -0,0 +1,70 @@ +import types from './actionTypes'; +import { getProjectFunc, getProjectDetailFunc, getProjectEntriesFunc, getBannerFunc, getProjectReadMe } from '../../services/project' + + +// 获取项目信息 +export const setProject = ({ owner,projectsId }) => { + return (dispatch) => { + return getProjectFunc(owner, projectsId).then(res => { + dispatch({ + type: types.GET_PROJECT_BASE, + payload: res.data + }); + return res.data + }) + } +} + +// 获取项目详情信息 +export const setProjectDetail = ({ owner,projectsId }) => { + return (dispatch) => { + return getProjectDetailFunc(owner, projectsId).then(res => { + dispatch({ + type: types.GET_PROJECT_DETAIL, + payload: res.data + }); + return res.data + }) + } +} + +// 获取文件列表 +export const setProjectEntries = ({ owner,projectsId, branch }) => { + return (dispatch) => { + return getProjectEntriesFunc(owner, projectsId, branch).then(res => { + dispatch({ + type: types.GET_PROJECT_ENTRIES, + payload: res.data + }); + return res.data + }) + } +} + +// 获取banner +export const setProjectMenuList = ({ owner,projectsId }) => { + return (dispatch) => { + return getBannerFunc(owner, projectsId).then(res => { + dispatch({ + type: types.GET_PROJECT_MENU, + payload: res.data + }); + return res.data + }) + } +} + +// 获取readMe +export const setProjectReadMe = ({ owner,projectsId, branch }) => { + return (dispatch) => { + return getProjectReadMe(owner, projectsId, branch).then(res => { + dispatch({ + type: types.GET_PROJECT_README, + payload: res.data + }); + return res.data + }) + } +} + + diff --git a/src/redux/reducers/index.js b/src/redux/reducers/index.js index afb58078c..b2f1e90d5 100644 --- a/src/redux/reducers/index.js +++ b/src/redux/reducers/index.js @@ -18,6 +18,7 @@ import commentReducer from './commentReducer'; import tpiReducer from './tpiReducer'; import staticReducer from './staticReducer'; import wxcodeReducer from './wxcodeReducer'; +import projectReducer from './projectReducer' export default combineReducers({ testReducer, @@ -30,5 +31,6 @@ export default combineReducers({ commentReducer, tpiReducer, staticReducer, - wxcodeReducer + wxcodeReducer, + projectReducer }); diff --git a/src/redux/reducers/projectReducer.js b/src/redux/reducers/projectReducer.js new file mode 100644 index 000000000..a0a46ffe1 --- /dev/null +++ b/src/redux/reducers/projectReducer.js @@ -0,0 +1,48 @@ +import types from "../actions/actionTypes"; + +const initialState = { + defaultDetail: '', // 当前项目基本信息 + projectBase: '', // 当前项目详情 + projectEntries: '', // 当前文件列表 + projectMenu: '', // 当前menu + projectReadMe: '', // 当前readme +}; + +const projectReducer = (state = initialState, action) => { + switch (action.type) { + case types.GET_PROJECT_DETAIL: + return { + ...state, + defaultDetail: action.payload + } + case types.GET_PROJECT_BASE: + return { + ...state, + projectBase: action.payload + } + case types.GET_PROJECT_ENTRIES: + return { + ...state, + projectEntries: action.payload + } + case types.GET_PROJECT_MENU: + return { + ...state, + projectMenu: action.payload + } + case types.GET_PROJECT_README: + return { + ...state, + projectReadMe: action.payload + } + default: + return { + ...state + } + } +} + +export default projectReducer; +export { + projectReducer +}; diff --git a/src/redux/stores/configureStore.js b/src/redux/stores/configureStore.js index de80c0b69..c10632860 100644 --- a/src/redux/stores/configureStore.js +++ b/src/redux/stores/configureStore.js @@ -12,7 +12,13 @@ import rootReducer from '../reducers'; const configureStore = () => createStore( rootReducer, + window.__initState__, applyMiddleware(thunk) ); export default configureStore; + +export const serverStore = ()=>{ + return createStore(rootReducer, applyMiddleware(thunk)); +} + diff --git a/src/services/project.js b/src/services/project.js new file mode 100644 index 000000000..c29599d78 --- /dev/null +++ b/src/services/project.js @@ -0,0 +1,30 @@ +import axios from 'axios'; + +const baseUrl = 'https://www.gitlink.org.cn/api' + +// +export async function getProjectFunc (owner, projectsId) { + console.log('4444444444444444444444444') + const url = `${baseUrl}/${owner}/${projectsId}/simple.json`; + return axios.get(url); +} + +export async function getProjectDetailFunc (owner, projectsId) { + const url = `${baseUrl}/${owner}/${projectsId}/detail.json`; + return axios.get(url); +} + +export async function getProjectEntriesFunc (owner, projectsId, branch) { + const url = `${baseUrl}/${owner}/${projectsId}/entries.json`; + return axios.get(url, {params: { ref: branch }}); +} + +export async function getBannerFunc (owner, projectsId) { + const url = `${baseUrl}/${owner}/${projectsId}/menu_list.json`; + return axios.get(url); +} + +export async function getProjectReadMe (owner, projectsId, branch) { + const url = `${baseUrl}/${owner}/${projectsId}/readme.json`; + return axios.get(url, {params: { ref: branch }}); +} \ No newline at end of file