Merge pull request 'ssr分支' (#606) from Eeeros/forgeplus-react:gitlink_ssr_test into gitlink_ssr
1
This commit is contained in:
commit
1dd20d3d26
36
.babelrc
36
.babelrc
|
|
@ -1,16 +1,28 @@
|
|||
{
|
||||
"presets": [
|
||||
"env",
|
||||
"react",
|
||||
"stage-2"
|
||||
// 移除 "es2015" 预设,因为它已经被 "env" 预设覆盖
|
||||
["@babel/preset-env" ],
|
||||
"@babel/preset-react" // 使用新的插件命名
|
||||
],
|
||||
"plugins": [[
|
||||
"transform-runtime",
|
||||
{
|
||||
"helpers": false,
|
||||
"polyfill": false,
|
||||
"regenerator": true,
|
||||
"moduleName": "babel-runtime"
|
||||
}
|
||||
],["transform-decorators-legacy"]]
|
||||
"plugins": [
|
||||
["import", { "libraryName": "antd", "libraryDirectory": "lib"}],
|
||||
// 使用新的插件命名
|
||||
["@babel/plugin-transform-runtime", {
|
||||
"corejs": 3,
|
||||
"helpers": true,
|
||||
"regenerator": true,
|
||||
"useESModules": false
|
||||
}],
|
||||
["@babel/plugin-proposal-decorators", {
|
||||
"legacy": true
|
||||
}],
|
||||
["@babel/plugin-syntax-dynamic-import"],
|
||||
["@babel/plugin-transform-class-properties"], // 使用新的插件命名
|
||||
"@babel/plugin-syntax-import-meta",
|
||||
"@babel/plugin-proposal-json-strings",
|
||||
"@babel/plugin-proposal-function-sent",
|
||||
"@babel/plugin-proposal-export-namespace-from",
|
||||
"@babel/plugin-proposal-numeric-separator",
|
||||
"@babel/plugin-proposal-throw-expressions"
|
||||
]
|
||||
}
|
||||
|
|
@ -38,6 +38,7 @@ bower_components
|
|||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/
|
||||
buildserver/
|
||||
src/.umi/
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
|
|
|
|||
39
README.md
39
README.md
|
|
@ -1,3 +1,42 @@
|
|||
# gitlink-ssr版本
|
||||
|
||||
|
||||
#### node版本
|
||||
12.x
|
||||
#### ng配置
|
||||
|
||||
```
|
||||
if ($http_user_agent ~* "googlebot|Mediapartners-Google|bingbot|google-structured-data-testing-tool|baiduspider|360Spider|Sogou Spider|Yahoo! Slurp China|^$")
|
||||
{#判断如果是网络爬虫,转发到node服务器下
|
||||
proxy_pass http://www.test.com:3000;
|
||||
break;
|
||||
}
|
||||
```
|
||||
|
||||
#### 依赖安装、运行
|
||||
|
||||
```bash
|
||||
// 依赖
|
||||
npm install
|
||||
npm install pm2 -g
|
||||
|
||||
// 打包
|
||||
npm run build
|
||||
npm run build:server
|
||||
|
||||
// 启动服务
|
||||
npm run pm2
|
||||
|
||||
```
|
||||
或者
|
||||
```bash
|
||||
./build.sh
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>前端react环境安装:</h3>
|
||||
<p>1、 安装node v6.9.x;此安装包含了node和npm。</p>
|
||||
<p>2、 安装cnpm(命令行): npm install -g cnpm --registry=https://registry.npm.taobao.org</p>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
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",
|
||||
component: app,
|
||||
}
|
||||
];
|
||||
|
||||
export const deepRoutes = [
|
||||
{
|
||||
key:"detail",
|
||||
path: "/:owner/:projectsId",
|
||||
component: Detail,
|
||||
}
|
||||
]
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
#!/bin/bash
|
||||
|
||||
# 确保脚本在当前目录执行
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
# 安装依赖
|
||||
npm install
|
||||
|
||||
# 构建项目
|
||||
npm run build
|
||||
|
||||
# 构建服务器端
|
||||
npm run build:server
|
||||
|
||||
# 使用PM2启动应用程序
|
||||
npm run pm2
|
||||
|
|
@ -47,6 +47,7 @@ module.exports = {
|
|||
appIndexJs: resolveApp('src/index.js'),
|
||||
appPackageJson: resolveApp('package.json'),
|
||||
appSrc: resolveApp('src'),
|
||||
serverSrc: resolveApp('server'),
|
||||
yarnLockFile: resolveApp('yarn.lock'),
|
||||
testsSetup: resolveApp('src/setupTests.js'),
|
||||
appNodeModules: resolveApp('node_modules'),
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
export const url = process.env.NODE_ENV === 'production' ? 'https://www.gitlink.org.cn' : 'http://172.20.32.202:4000'
|
||||
|
|
@ -273,12 +273,11 @@ module.exports = {
|
|||
reactPath:'react.production.min.js',
|
||||
}),
|
||||
new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw),
|
||||
|
||||
// Add module names to factory functions so they appear in browser profiler.
|
||||
new webpack.NamedModulesPlugin(),
|
||||
// Makes some environment variables available to the JS code, for example:
|
||||
// if (process.env.NODE_ENV === 'development') { ... }. See `./env.js`.
|
||||
new webpack.DefinePlugin(env.stringified),
|
||||
new webpack.DefinePlugin({ ...env.stringified, __SERVER__: 'false',__CLIENT__: 'true' }),
|
||||
// This is necessary to emit hot updates (currently CSS only):
|
||||
new webpack.HotModuleReplacementPlugin(),
|
||||
// Watcher doesn't work well if you mistype casing in a path so we use
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
@ -16,8 +17,9 @@ const TerserWebpackPlugin = require('terser-webpack-plugin');
|
|||
const paths = require("./paths");
|
||||
const getClientEnvironment = require("./env");
|
||||
|
||||
let publicPath = "/react/build/";
|
||||
let publicPath = "/build/";
|
||||
const publicUrl = publicPath.slice(0, -1);
|
||||
|
||||
// const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== "false";
|
||||
const shouldUseSourceMap = process.env.NODE_ENV !== "production";
|
||||
const env = getClientEnvironment(publicPath,'production.min');
|
||||
|
|
@ -116,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"]
|
||||
],
|
||||
},
|
||||
|
|
@ -294,12 +299,12 @@ module.exports = {
|
|||
minifyURLs: true,
|
||||
},
|
||||
}),
|
||||
new webpack.DefinePlugin({ ...env.stringified, __SERVER__: 'false',__CLIENT__: 'true' }),
|
||||
new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw),
|
||||
// Makes some environment variables available to the JS code, for example:
|
||||
// if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
|
||||
// It is absolutely essential that NODE_ENV was set to production here.
|
||||
// Otherwise React will be compiled in the very slow development mode.
|
||||
new webpack.DefinePlugin(env.stringified),
|
||||
|
||||
new MiniCssExtractPlugin({
|
||||
filename: "static/css/[name].[contenthash:8].css",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,243 @@
|
|||
const autoprefixer = require("autoprefixer");
|
||||
const path = require("path");
|
||||
const webpack = require("webpack");
|
||||
const HtmlWebpackPlugin = require("html-webpack-plugin");
|
||||
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
|
||||
const ManifestPlugin = require("webpack-manifest-plugin");
|
||||
const InterpolateHtmlPlugin = require("react-dev-utils/InterpolateHtmlPlugin");
|
||||
const SWPrecacheWebpackPlugin = require("sw-precache-webpack-plugin");
|
||||
const eslintFormatter = require("react-dev-utils/eslintFormatter");
|
||||
const ModuleScopePlugin = require("react-dev-utils/ModuleScopePlugin");
|
||||
const MonacoWebpackPlugin = require("monaco-editor-webpack-plugin");
|
||||
const TerserJSPlugin = require("terser-webpack-plugin");
|
||||
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
|
||||
const TerserWebpackPlugin = require('terser-webpack-plugin');
|
||||
const paths = require("./paths");
|
||||
const getClientEnvironment = require("./env");
|
||||
|
||||
let publicPath = "/react/buildserver/";
|
||||
// const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== "false";
|
||||
const shouldUseSourceMap = true;
|
||||
const env = getClientEnvironment(publicPath,'development');
|
||||
const nodeExternals = require("webpack-node-externals");
|
||||
|
||||
const serverConfig = {
|
||||
target:"node", //由于输出代码的运行环境是node,源码中依赖的node原生模块没必要打包进去;为了不把nodejs内置模块打包进输出文件中,例如: fs net模块等;
|
||||
mode: "development",
|
||||
entry: path.resolve(__dirname,"../server/index.js"),
|
||||
output:{
|
||||
filename:"bundle.js",
|
||||
path: path.resolve(__dirname,"../buildserver")
|
||||
},
|
||||
externals: [
|
||||
nodeExternals(), // 忽略 Node.js 核心模块
|
||||
// 自定义的外部模块
|
||||
{ "react": "React", "react-dom": "ReactDOM" }
|
||||
],
|
||||
resolve: {
|
||||
// This allows you to set a fallback for where Webpack should look for modules.
|
||||
// We placed these paths second because we want `node_modules` to "win"
|
||||
// if there are any conflicts. This matches Node resolution mechanism.
|
||||
// https://github.com/facebookincubator/create-react-app/issues/253
|
||||
modules: ["node_modules", paths.appNodeModules].concat(
|
||||
// It is guaranteed to exist because we tweak it in `env.js`
|
||||
process.env.NODE_PATH.split(path.delimiter).filter(Boolean)
|
||||
),
|
||||
// These are the reasonable defaults supported by the Node ecosystem.
|
||||
// We also include JSX as a common component filename extension to support
|
||||
// some tools, although we do not recommend using it, see:
|
||||
// https://github.com/facebookincubator/create-react-app/issues/290
|
||||
// `web` extension prefixes have been added for better support
|
||||
// for React Native Web.
|
||||
extensions: [".web.js", ".mjs", ".js", ".json", ".web.jsx", ".jsx"],
|
||||
alias: {
|
||||
educoder: __dirname + "/../src/common/educoder.js",
|
||||
// Support React Native Web
|
||||
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
|
||||
"react-native": "react-native-web",
|
||||
},
|
||||
plugins: [
|
||||
// Prevents users from importing files from outside of src/ (or node_modules/).
|
||||
// This often causes confusion because we only process files within src/ with babel.
|
||||
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
|
||||
// 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]),
|
||||
// ["transform-remove-console"]
|
||||
],
|
||||
},
|
||||
module: {
|
||||
strictExportPresence: true,
|
||||
rules: [
|
||||
{
|
||||
test: /\.(js|jsx|mjs)$/,
|
||||
enforce: "pre",
|
||||
use: [
|
||||
{
|
||||
options: {
|
||||
formatter: eslintFormatter,
|
||||
eslintPath: require.resolve("eslint"),
|
||||
},
|
||||
loader: require.resolve("eslint-loader"),
|
||||
},
|
||||
],
|
||||
include: [paths.appSrc, paths.serverSrc],
|
||||
},
|
||||
{
|
||||
// "oneOf" will traverse all following loaders until one will
|
||||
// match the requirements. When no loader matches it will fall
|
||||
// back to the "file" loader at the end of the loader list.
|
||||
oneOf: [
|
||||
// "url" loader works just like "file" loader but it also embeds
|
||||
// assets smaller than specified size as data URLs to avoid requests.
|
||||
{
|
||||
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
|
||||
loader: require.resolve("url-loader"),
|
||||
options: {
|
||||
limit: 10000,
|
||||
name: "static/media/[name].[hash:8].[ext]",
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.(js|jsx|mjs)$/,
|
||||
include: [paths.appSrc, paths.serverSrc],
|
||||
// exclude: /node_modules/,
|
||||
loader: "babel-loader",
|
||||
options: {
|
||||
cacheDirectory: true,
|
||||
presets: [
|
||||
require.resolve('@babel/preset-env'),
|
||||
require.resolve('@babel/preset-react'),
|
||||
],
|
||||
plugins: [
|
||||
require.resolve('@babel/plugin-transform-async-to-generator'),
|
||||
require.resolve('@babel/plugin-syntax-dynamic-import'),
|
||||
require.resolve('@babel/plugin-proposal-class-properties'),
|
||||
require.resolve('@babel/plugin-proposal-export-default-from'),
|
||||
require.resolve('@babel/plugin-transform-runtime'),
|
||||
require.resolve('@babel/plugin-transform-modules-commonjs'),
|
||||
require.resolve('babel-plugin-dynamic-import-webpack'),
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: [
|
||||
{
|
||||
loader: MiniCssExtractPlugin.loader,
|
||||
options: {
|
||||
publicPath,
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: require.resolve("css-loader"),
|
||||
options: {
|
||||
importLoaders: 1,
|
||||
sourceMap: shouldUseSourceMap,
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: require.resolve("postcss-loader"),
|
||||
options: {
|
||||
ident: "postcss",
|
||||
plugins: () => [
|
||||
require("postcss-flexbugs-fixes"),
|
||||
autoprefixer({
|
||||
browsers: [
|
||||
">1%",
|
||||
"last 4 versions",
|
||||
"Firefox ESR",
|
||||
"not ie < 9", // React doesn't support IE8 anyway
|
||||
],
|
||||
flexbox: "no-2009",
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.scss$/,
|
||||
use: [
|
||||
{
|
||||
loader: MiniCssExtractPlugin.loader,
|
||||
options: {
|
||||
publicPath,
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: require.resolve("css-loader"),
|
||||
options: {
|
||||
importLoaders: 1,
|
||||
sourceMap: shouldUseSourceMap,
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: require.resolve("sass-loader"),
|
||||
},
|
||||
{
|
||||
loader: 'sass-resources-loader',
|
||||
options: {
|
||||
resources: ['src/global.scss']
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.less$/,
|
||||
use: [{
|
||||
loader: 'isomorphic-style-loader',
|
||||
}, {
|
||||
loader: 'css-loader', // translates CSS into CommonJS
|
||||
}, {
|
||||
loader: 'less-loader', // compiles Less to CSS
|
||||
options: {
|
||||
modifyVars: {
|
||||
'primary-color': '#466aff',
|
||||
'link-color': '#466aff',
|
||||
},
|
||||
javascriptEnabled: true,
|
||||
},
|
||||
}]
|
||||
},
|
||||
// "file" loader makes sure assets end up in the `build` folder.
|
||||
// When you `import` an asset, you get its filename.
|
||||
// This loader doesn't use a "test" so it will catch all modules
|
||||
// that fall through the other loaders.
|
||||
{
|
||||
loader: require.resolve("file-loader"),
|
||||
// Exclude `js` files to keep "css" loader working as it injects
|
||||
// it's runtime that would otherwise processed through "file" loader.
|
||||
// Also exclude `html` and `json` extensions so they get processed
|
||||
// by webpacks internal loaders.
|
||||
exclude: [/\.(js|jsx|mjs)$/, /\.html$/, /\.json$/],
|
||||
options: {
|
||||
name: "static/media/[name].[contenthash:8].[ext]",
|
||||
},
|
||||
},
|
||||
// ** STOP ** Are you adding a new loader?
|
||||
// Make sure to add the new loader(s) before the "file" loader.
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({ ...env.stringified, __SERVER__: 'true',__CLIENT__: 'false' }),
|
||||
new MiniCssExtractPlugin({
|
||||
filename: "static/css/[name].[contenthash:8].css",
|
||||
chunkFilename: "static/css/[name].[contenthash:8].chunk.css",
|
||||
}),
|
||||
new ManifestPlugin({
|
||||
fileName: "asset-manifest.json",
|
||||
}),
|
||||
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
|
||||
new MonacoWebpackPlugin({
|
||||
features: ["coreCommands", "find"],
|
||||
languages: ['plaintext','apex', 'azcli', 'bat', 'clojure', 'coffee', 'cpp', 'csharp', 'csp', 'css', 'dockerfile', 'fsharp', 'go', 'handlebars', 'html', 'ini', 'java', 'javascript', 'json', 'less', 'lua', 'markdown', 'msdax', 'mysql', 'objective', 'perl', 'pgsql', 'php', 'postiats', 'powerquery', 'powershell', 'pug', 'python', 'r', 'razor', 'redis', 'redshift', 'ruby', 'rust', 'sb', 'scheme', 'scss', 'shell', 'solidity', 'sql', 'st', 'swift', 'typescript', 'vb', 'xml', 'yaml']
|
||||
}),
|
||||
new webpack.NamedChunksPlugin(),
|
||||
new webpack.HashedModuleIdsPlugin(),
|
||||
],
|
||||
};
|
||||
|
||||
module.exports = serverConfig;
|
||||
File diff suppressed because it is too large
Load Diff
84
package.json
84
package.json
|
|
@ -3,6 +3,24 @@
|
|||
"version": "3.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/plugin-proposal-class-properties": "^7.0.0",
|
||||
"@babel/plugin-proposal-decorators": "^7.0.0",
|
||||
"@babel/plugin-proposal-export-default-from": "^7.23.3",
|
||||
"@babel/plugin-proposal-export-namespace-from": "^7.0.0",
|
||||
"@babel/plugin-proposal-function-sent": "^7.0.0",
|
||||
"@babel/plugin-proposal-json-strings": "^7.0.0",
|
||||
"@babel/plugin-proposal-numeric-separator": "^7.0.0",
|
||||
"@babel/plugin-proposal-throw-expressions": "^7.0.0",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.0.0",
|
||||
"@babel/plugin-syntax-import-meta": "^7.0.0",
|
||||
"@babel/plugin-transform-async-to-generator": "^7.0.0",
|
||||
"@babel/plugin-transform-runtime": "^7.23.6",
|
||||
"@babel/preset-env": "^7.23.6",
|
||||
"@babel/preset-react": "^7.23.3",
|
||||
"@babel/preset-stage-2": "^7.8.3",
|
||||
"@babel/runtime": "^7.0.0-beta.46",
|
||||
"@babel/runtime-corejs3": "^7.23.6",
|
||||
"@monaco-editor/react": "^2.3.0",
|
||||
"@novnc/novnc": "^1.1.0",
|
||||
"@wangeditor/editor": "^5.1.23",
|
||||
|
|
@ -13,6 +31,10 @@
|
|||
"array-flatten": "^2.1.2",
|
||||
"autoprefixer": "7.1.6",
|
||||
"axios": "^0.24.0",
|
||||
"babel-core": "^7.0.0-bridge.0",
|
||||
"babel-jest": "^23.4.2",
|
||||
"babel-plugin-antd": "^0.5.1",
|
||||
"babel-plugin-dynamic-import-webpack": "^1.1.0",
|
||||
"bizcharts": "^3.5.8",
|
||||
"bundle-loader": "^0.5.6",
|
||||
"chalk": "1.1.3",
|
||||
|
|
@ -20,22 +42,29 @@
|
|||
"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",
|
||||
"dotenv": "4.0.0",
|
||||
"dotenv-expand": "4.2.0",
|
||||
"echarts": "^4.9.0",
|
||||
"echarts-wordcloud": "^2.0.0",
|
||||
"editor.md": "^1.5.0",
|
||||
"express": "^4.18.2",
|
||||
"flv.js": "^1.5.0",
|
||||
"fs-extra": "3.0.1",
|
||||
"http-proxy-middleware": "^2.0.6",
|
||||
"i18next": "^23.4.5",
|
||||
"immutability-helper": "^2.6.6",
|
||||
"install": "^0.12.2",
|
||||
"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",
|
||||
"katex": "^0.11.1",
|
||||
"less": "^3.13.1",
|
||||
"localforage": "^1.10.0",
|
||||
|
|
@ -88,6 +117,7 @@
|
|||
"react-redux": "5.0.7",
|
||||
"react-resizable": "^1.10.1",
|
||||
"react-router": "^4.2.0",
|
||||
"react-router-config": "^5.1.1",
|
||||
"react-router-dom": "^4.2.2",
|
||||
"react-slick": "^0.28.1",
|
||||
"react-split-pane": "^0.1.91",
|
||||
|
|
@ -104,23 +134,31 @@
|
|||
"slick-carousel": "^1.8.1",
|
||||
"store": "^2.0.12",
|
||||
"styled-components": "^4.4.1",
|
||||
"webpack-node-externals": "^3.0.0",
|
||||
"weixin-js-sdk": "^1.6.0",
|
||||
"whatwg-fetch": "2.0.3",
|
||||
"winston": "^3.11.0",
|
||||
"winston-daily-rotate-file": "^4.7.1",
|
||||
"wrap-md-editor": "^0.2.20",
|
||||
"xss": "^1.0.14",
|
||||
"xterm": "4.8.1",
|
||||
"xterm-addon-fit": "0.4.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node --max_old_space_size=15360 scripts/start.js",
|
||||
"build": "cross-env NODE_ENV=production node --max_old_space_size=15360 scripts/build.js",
|
||||
"start": "node scripts/start.js",
|
||||
"build": "cross-env NODE_ENV=production babel-node --max_old_space_size=15360 scripts/build.js",
|
||||
"build:server": "cross-env NODE_ENV=production webpack --config config/webpack.server.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",
|
||||
"pre-build": "NODE_ENV=preBuild node --max_old_space_size=15360 scripts/build.js",
|
||||
"gen_stats": "NODE_ENV=production webpack --profile --config=./config/webpack.config.prod.js --json > stats.json",
|
||||
"ana": "webpack-bundle-analyzer ./stats.json",
|
||||
"analyze": "npm run build -- --stats && webpack-bundle-analyzer build/bundle-stats.json",
|
||||
"analyz": "NODE_ENV=production npm_config_report=true npm run build"
|
||||
"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",
|
||||
"pm2": "pm2 start pm2.json"
|
||||
},
|
||||
"jest": {
|
||||
"collectCoverageFrom": [
|
||||
|
|
@ -156,46 +194,21 @@
|
|||
"node"
|
||||
]
|
||||
},
|
||||
"babel": {
|
||||
"presets": [
|
||||
"react",
|
||||
"react-app"
|
||||
],
|
||||
"plugins": [
|
||||
[
|
||||
"import",
|
||||
{
|
||||
"libraryName": "antd",
|
||||
"libraryDirectory": "lib",
|
||||
"style": "css"
|
||||
},
|
||||
"ant"
|
||||
],
|
||||
"syntax-dynamic-import"
|
||||
]
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app"
|
||||
},
|
||||
"proxy": "http://172.20.32.202:4000",
|
||||
"port": "3007",
|
||||
"devDependencies": {
|
||||
"@babel/runtime": "7.0.0-beta.51",
|
||||
"babel-cli": "^6.26.0",
|
||||
"babel-core": "^6.26.0",
|
||||
"babel-eslint": "7.2.3",
|
||||
"babel-jest": "20.0.3",
|
||||
"babel-loader": "7.1.2",
|
||||
"@babel/core": "^7.23.6",
|
||||
"@babel/node": "^7.0.0",
|
||||
"@babel/plugin-proposal-decorators": "^7.0.0",
|
||||
"@babel/polyfill": "^7.0.0",
|
||||
"@babel/preset-react": "^7.0.0",
|
||||
"babel-loader": "^8.3.0",
|
||||
"babel-plugin-import": "^1.13.0",
|
||||
"babel-plugin-syntax-dynamic-import": "^6.18.0",
|
||||
"babel-plugin-transform-decorators-legacy": "^1.3.5",
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"babel-preset-es2015": "^6.24.1",
|
||||
"babel-preset-react": "^6.24.1",
|
||||
"babel-preset-react-app": "^3.1.1",
|
||||
"babel-preset-stage-2": "^6.24.1",
|
||||
"babel-runtime": "6.26.0",
|
||||
"babel-upgrade": "^1.0.1",
|
||||
"case-sensitive-paths-webpack-plugin": "2.1.1",
|
||||
"compression-webpack-plugin": "^1.1.12",
|
||||
"concat": "^1.0.3",
|
||||
|
|
@ -214,6 +227,7 @@
|
|||
"less-loader": "^4.1.0",
|
||||
"mockjs": "^1.1.0",
|
||||
"node-sass": "^4.14.1",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"optimize-css-assets-webpack-plugin": "^5.0.3",
|
||||
"postcss-loader": "2.0.8",
|
||||
"purgecss": "^2.1.2",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"apps": [{
|
||||
"name": "gitlink-ssr",
|
||||
"script": "buildserver/bundle.js",
|
||||
"watch": false,
|
||||
"log_date_format": "YYYY-MM-DD HH:mm Z",
|
||||
"exec_mode": "cluster",
|
||||
"max_memory_restart": "500M",
|
||||
"env": {
|
||||
"NODE_ENV": "production"
|
||||
},
|
||||
"instances": 4
|
||||
}]
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
import express from "express";
|
||||
import "./window"
|
||||
import {render} from "./render";
|
||||
import { url } from "../config/ssrUrl";
|
||||
const { createProxyMiddleware } = require('http-proxy-middleware');
|
||||
|
||||
const app = express();
|
||||
app.use('/build', express.static('build'));
|
||||
|
||||
const targetServer = url; // Java 服务器的地址
|
||||
const options = {
|
||||
target: targetServer,
|
||||
changeOrigin: true, // 允许在请求头中更改主机
|
||||
timeout: 30000
|
||||
};
|
||||
|
||||
// 设置代理
|
||||
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) {
|
||||
render(req,res);
|
||||
})
|
||||
|
||||
const port = 3000
|
||||
|
||||
console.log(`\n==> 🌎 Listening on port ${port}. Open up http://localhost:${port}/ in your browser.\n`)
|
||||
app.listen(port, '0.0.0.0');
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
const { createLogger, format, transports } = require("winston");
|
||||
require("winston-daily-rotate-file");
|
||||
|
||||
const customFormat = format.combine(
|
||||
format.timestamp({ format: "MMM-DD-YYYY HH:mm:ss" }),
|
||||
format.align(),
|
||||
format.printf((i) => `${i.level}: ${[i.timestamp]}: ${i.message}`)
|
||||
);
|
||||
const defaultOptions = {
|
||||
format: customFormat,
|
||||
datePattern: "YYYY-MM-DD",
|
||||
zippedArchive: true,
|
||||
maxSize: "20m",
|
||||
maxFiles: "14d",
|
||||
};
|
||||
|
||||
const Logger = createLogger({
|
||||
format: customFormat,
|
||||
transports: [
|
||||
new transports.DailyRotateFile({
|
||||
filename: "logs/info-%DATE%.log",
|
||||
level: "info",
|
||||
...defaultOptions,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
export default Logger
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
import React from "react";
|
||||
import {renderToString} from "react-dom/server";
|
||||
import {StaticRouter, matchPath} from "react-router-dom";
|
||||
|
||||
import Routes, { deepRoutes } from "../Routes";
|
||||
import { Provider } from "react-redux";
|
||||
import { serverStore } from "../src/redux/stores/configureStore";
|
||||
import App from "../src/App";
|
||||
import { Route } from 'react-router-dom';
|
||||
import { setDefaultMeta } from '../src/common/UrlTool'
|
||||
import Logger from "./log";
|
||||
|
||||
export const render = async (req,res)=>{
|
||||
let _route = null,
|
||||
_match = null;
|
||||
Logger.info(`${req.url} UA:${req.headers['user-agent']}`)
|
||||
|
||||
let 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((
|
||||
<Provider store={store}>
|
||||
<StaticRouter location={req.path} context={context}>
|
||||
<Route path='/:owner/:projectsId' exact component={App}></Route>
|
||||
</StaticRouter>
|
||||
</Provider>
|
||||
))
|
||||
|
||||
if (!content) {
|
||||
// 未匹配到详情页恢复默认header
|
||||
setDefaultMeta()
|
||||
} else {
|
||||
// 匹配到服务端渲染页面注入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('<div id="root" class="page -layout-v -fit widthunit"></div>',`<div id="root" class="page -layout-v -fit widthunit">${rootString}</div>`);
|
||||
return data;
|
||||
}
|
||||
res.send(prepHTML(html, content))
|
||||
|
||||
store = null
|
||||
}
|
||||
|
|
@ -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`)
|
||||
}
|
||||
|
|
@ -0,0 +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 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);
|
||||
// });
|
||||
25
src/App.js
25
src/App.js
|
|
@ -8,7 +8,7 @@ import {
|
|||
} from 'react-router-dom';
|
||||
import axios from 'axios';
|
||||
import LoginDialog from './modules/login/LoginDialog';
|
||||
import 'babel-polyfill';
|
||||
// import 'babel-polyfill';
|
||||
import Loading from './Loading';
|
||||
|
||||
import Loadable from 'react-loadable';
|
||||
|
|
@ -22,11 +22,10 @@ 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'
|
||||
import OrganizeIndex from './forge/Team/Index'
|
||||
|
||||
const store = configureStore();
|
||||
window.marked = marked;
|
||||
const theme = createMuiTheme({
|
||||
palette: {
|
||||
|
|
@ -77,15 +76,15 @@ 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'),
|
||||
loading: Loading,
|
||||
})
|
||||
// const OrganizeIndex = Loadable({
|
||||
// loader: () => import('./forge/Team/Index'),
|
||||
// loading: Loading,
|
||||
// })
|
||||
|
||||
const Search = Loadable({
|
||||
loader: () => import('./modules/search/'),
|
||||
|
|
@ -328,7 +327,6 @@ class App extends Component {
|
|||
render() {
|
||||
const { pathType, pathName, mygetHelmetapi } = this.state;
|
||||
return (
|
||||
<Provider store={store}>
|
||||
<ConfigProvider locale={zhCN}>
|
||||
<MuiThemeProvider theme={theme}>
|
||||
<LoginDialog {...this.props} {...this.state} Modifyloginvalue={() => this.Modifyloginvalue()}></LoginDialog>
|
||||
|
|
@ -536,7 +534,6 @@ class App extends Component {
|
|||
</Switch>
|
||||
</MuiThemeProvider>
|
||||
</ConfigProvider>
|
||||
</Provider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
@ -63,7 +63,7 @@ export function initAxiosInterceptors(props) {
|
|||
});
|
||||
|
||||
axios.interceptors.response.use(function (response) {
|
||||
if (response === undefined) {
|
||||
if (response === undefined || !response.data) {
|
||||
return
|
||||
}
|
||||
const config = response.config;
|
||||
|
|
@ -94,13 +94,13 @@ 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不跳转
|
||||
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');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ import md5 from 'md5';
|
|||
import {Input} from "antd";
|
||||
const { Search } = Input;
|
||||
|
||||
const $ = window.$;
|
||||
const isDev = window.location.port == 3007;
|
||||
const isdev2= window.location.hostname ==='www.educoder.net'
|
||||
// const $ = window.$;
|
||||
const isDev = __SERVER__ ? false : window.location.port == 3007;
|
||||
const isdev2= __SERVER__ ? false : window.location.hostname ==='www.educoder.net'
|
||||
export const TEST_HOST = "https://testforgeplus.trustie.net/"
|
||||
export function getImageUrl(path) {
|
||||
// https://www.educoder.net
|
||||
|
|
@ -40,7 +40,7 @@ export function getImageUrlAbsolute(path) {
|
|||
// const local = 'http://localhost:3000'
|
||||
path && !path.startsWith('/') && !path.startsWith('http') && (path = '/'.concat(path));
|
||||
const local = 'https://testforgeplus.trustie.net';
|
||||
const prod = window.location.origin;
|
||||
const prod = __SERVER__ ? local : window.location.origin;
|
||||
if (isDev) {
|
||||
return `${local}${path}`
|
||||
}else{
|
||||
|
|
@ -198,7 +198,7 @@ function railsgettimess(proxy) {
|
|||
}
|
||||
}})
|
||||
|
||||
window.setTimeout(function () {
|
||||
setTimeout(function () {
|
||||
checkSubmitFlgs=false;
|
||||
}, 2500);
|
||||
}
|
||||
|
|
@ -295,13 +295,13 @@ export function turnbar(str){
|
|||
let s = str;
|
||||
if(s && s.length>0){
|
||||
if(s.indexOf("%")>-1){
|
||||
s = s.replaceAll('%','_25');
|
||||
s = s.split('%').join('_25');
|
||||
}
|
||||
if(s.indexOf("#")>-1){
|
||||
s = s.replaceAll('#','%23');
|
||||
s = s.split('#').join('%23');
|
||||
}
|
||||
if(s.indexOf("/")>-1){
|
||||
s = s.replaceAll('/','%2F');
|
||||
s = s.split('/').join('%2F');
|
||||
}
|
||||
}
|
||||
return s;
|
||||
|
|
@ -310,13 +310,13 @@ export function returnbar(str){
|
|||
let s = str;
|
||||
if(s && s.length>0){
|
||||
if(str.indexOf("_25")>-1){
|
||||
s = s.replaceAll('_25','%');
|
||||
s = s.split('_25').join('%');
|
||||
}
|
||||
if(s.indexOf("%23")>-1){
|
||||
s = s.replaceAll('%23','#');
|
||||
s = s.split('%23').join('#');
|
||||
}
|
||||
if(s.indexOf("%2F")>-1){
|
||||
s = s.replaceAll('%2F','/');
|
||||
s = s.split('%2F').join('/');
|
||||
}
|
||||
}
|
||||
return s;
|
||||
|
|
@ -348,6 +348,9 @@ export function setSeoMeta(keyWords, title, description, url, owner, projectId)
|
|||
if(owner)keyStatement += owner;
|
||||
if(projectId)keyStatement += `/${projectId}`;
|
||||
|
||||
if (domObj) {
|
||||
document = domObj.window.document
|
||||
}
|
||||
document.querySelector(`meta[property='og:title']`).content = title + keyStatement + ' for gitlink' + keyStatement + ' for git';
|
||||
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';
|
||||
|
|
@ -364,5 +367,27 @@ 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"]')
|
||||
}
|
||||
}
|
||||
|
||||
export function setDefaultMeta() {
|
||||
|
||||
if (domObj) {
|
||||
document = domObj.window.document
|
||||
}
|
||||
document.querySelector(`meta[property='og:title']`).content = 'GitLink | 确实开源';
|
||||
document.querySelector(`meta[property='og:url']`).content = 'https://gitlink.org.cn/';
|
||||
document.querySelector(`meta[property='og:description']`).content = 'GitLink,新一代开源创新服务平台 分布式协作开发 一站式过程管理 高效流水线运维 多层次代码分析 多维度用户画像 分布式协作开发 基于Git打造分布式代码托管环境';
|
||||
document.querySelector(`meta[property='og:image:alt']`).content = 'GitLink | 确实开源';
|
||||
document.querySelector('meta[name="Keywords"]').content= 'gitLink,GitLink,gitlink,git,trustie,trustieforge,forge,开源,确实开源,代码托管,Git,开源,内源,项目管理,版本控制,开源代码,代码分享,项目协作,开源项目托管,免费代码托管,Git代码托管,Git托管服务,确实让创建更美好,协同开发平台';
|
||||
document.querySelector(`meta[name='description']`).content = 'GitLink,新一代开源创新服务平台 分布式协作开发 一站式过程管理 高效流水线运维 多层次代码分析 多维度用户画像 分布式协作开发 基于Git打造分布式代码托管环境';
|
||||
document.querySelector(`meta[name='go-import']`).content ='gitlink.org.cn git https://gitlink.org.cn';
|
||||
document.querySelector(`meta[name='octolytics-dimension-user_login']`).content = 'GitLink';
|
||||
document.querySelector(`meta[name='octolytics-dimension-repository_nwo']`).content = 'GitLink';
|
||||
document.querySelector(`meta[name='octolytics-dimension-repository_network_root_nwo']`).content = 'GitLink';
|
||||
document.querySelector(`meta[name='twitter:title']`).content = 'GitLink | 确实开源';
|
||||
document.querySelector(`meta[name='twitter:description']`).content = 'GitLink,新一代开源创新服务平台 分布式协作开发 一站式过程管理 高效流水线运维 多层次代码分析 多维度用户画像 分布式协作开发 基于Git打造分布式代码托管环境';
|
||||
document.querySelector(`link[rel='canonical']`).href = 'https://gitlink.org.cn';
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
const queryString = {
|
||||
export const queryString = {
|
||||
stringify: function(params) {
|
||||
let paramsUrl = '';
|
||||
for (let key in params) {
|
||||
|
|
@ -47,5 +47,4 @@ const queryString = {
|
|||
./node_modules/_query-string@6.1.0@query-string/index.js:8
|
||||
|
||||
Read more here: http://bit.ly/2tRViJ9
|
||||
*/
|
||||
module.exports = queryString
|
||||
*/
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
import './index.scss';
|
||||
import React, { useState } from 'react';
|
||||
import { Form, Button, Input } from 'antd';
|
||||
import QuillForEditor from '../../quillForEditor';
|
||||
// import QuillForEditor from '../../quillForEditor';
|
||||
const FormItem = Form.Item;
|
||||
|
||||
function CommentForm(props) {
|
||||
|
|
@ -116,7 +116,7 @@ function CommentForm(props) {
|
|||
)
|
||||
}
|
||||
|
||||
<QuillForEditor
|
||||
{/* <QuillForEditor
|
||||
imgAttrs={{ width: '60px', height: '30px' }}
|
||||
wrapStyle={{
|
||||
height: showQuill ? 'auto' : '0px',
|
||||
|
|
@ -131,7 +131,7 @@ function CommentForm(props) {
|
|||
value={ctx}
|
||||
showUploadImage={handleShowImage}
|
||||
onContentChange={handleContentChange}
|
||||
/>
|
||||
/> */}
|
||||
</FormItem>
|
||||
<FormItem style={{ textAlign: 'right', display: showQuill ? 'block' : 'none' }}>
|
||||
<Button onClick={handleCancle}>取消</Button>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ export {
|
|||
} from './UrlTool';
|
||||
|
||||
export { setmiyah as setmiyah } from './Component';
|
||||
export { default as queryString } from './UrlTool2';
|
||||
export { queryString } from './UrlTool2';
|
||||
|
||||
export { SnackbarHOC as SnackbarHOC } from './SnackbarHOC';
|
||||
|
||||
|
|
@ -68,7 +68,7 @@ export { default as ActionBtn } from './course/ActionBtn'
|
|||
|
||||
export { default as MarkdownToHtml } from './components/markdown/MarkdownToHtml'
|
||||
|
||||
export { default as QuillForEditor } from './quillForEditor'
|
||||
// export { default as QuillForEditor } from './quillForEditor'
|
||||
|
||||
export { default as Clappr } from './components/media/Clappr'
|
||||
export { default as AliyunUploader } from './components/media/AliyunUploader'
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import './index.scss'
|
||||
import 'quill/dist/quill.core.css' // 核心样式
|
||||
import 'quill/dist/quill.snow.css' // 有工具栏
|
||||
import 'quill/dist/quill.bubble.css' // 无工具栏
|
||||
// import 'quill/dist/quill.core.css' // 核心样式
|
||||
// import 'quill/dist/quill.snow.css' // 有工具栏
|
||||
// import 'quill/dist/quill.bubble.css' // 无工具栏
|
||||
import './font.css'
|
||||
import React, { useState, useRef, useEffect } from 'react'
|
||||
import Quill from 'quill'
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useEffect, useRef, useMemo , useState } from 'react'
|
||||
import 'katex/dist/katex.min.css';
|
||||
// import 'katex/dist/katex.min.css';
|
||||
import marked, { getTocContent, cleanToc, getMathExpressions, resetMathExpressions } from '../common/marked';
|
||||
import 'code-prettify';
|
||||
import dompurify from 'dompurify';
|
||||
|
|
@ -93,7 +93,7 @@ export default ({
|
|||
for(var x=0;x<issues.length;x++){
|
||||
let item = issues[x];
|
||||
let content = item.id ? `<a href="`+`/${owner}/${projectsId}/issues/${item.project_issues_index}`+`">#${item.project_issues_index}:${item.subject}</a>` : `<span>#${item.project_issues_index}(已删除)</span>`;
|
||||
rs = rs.replaceAll(`<a href="`+`/${owner}/${projectsId}/issues/${item.project_issues_index}`+`">#${item.project_issues_index}</a>`,content);
|
||||
rs = rs.split(`<a href="`+`/${owner}/${projectsId}/issues/${item.project_issues_index}`+`">#${item.project_issues_index}</a>`).join(content);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -102,7 +102,7 @@ export default ({
|
|||
return renderToString(_unescape(expression) || '', { displayMode: type === 'block', throwOnError: false, output: 'html' })
|
||||
})
|
||||
rs = dompurify.sanitize(rs.replace(/▁/g, "▁▁▁"))
|
||||
rs = rs.replaceAll('<img ', `<img onerror="javascript:this.src='${ imgError }';"`)
|
||||
rs = rs.split('<img ').join(`<img onerror="javascript:this.src='${ imgError }';"`) // 替换replaceall
|
||||
resetMathExpressions()
|
||||
return rs
|
||||
}, [str,issues]);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { WhiteBack , Box , LongWidth , ShortWidth , Gap , AlignCenter , FlexAJ
|
|||
import { Dropdown , Menu , Divider , Spin, Button , Typography } from 'antd';
|
||||
import { getImageUrl , turnbar , returnbar } from "educoder";
|
||||
import { Link } from 'react-router-dom';
|
||||
import { connect } from 'react-redux';
|
||||
import { truncateCommitId } from "../common/util";
|
||||
import CloneAddress from '../Branch/CloneAddress';
|
||||
|
||||
|
|
@ -12,7 +13,7 @@ import axios from 'axios';
|
|||
import Path from './CoderDepotPath';
|
||||
import Catalogue from './CoderDepotCatalogue';
|
||||
import ReadMe from './CoderDepotReadme';
|
||||
import CoderRootFileDetail from './CoderRootFileDetail';
|
||||
// import CoderRootFileDetail from './CoderRootFileDetail';
|
||||
import './Index.scss';
|
||||
import Releases from '../Component/Releases';
|
||||
import Contributors from '../Component/Contributors';
|
||||
|
|
@ -28,30 +29,34 @@ import RenderHtml from '../../components/render-html';
|
|||
*/
|
||||
function CoderDepot(props){
|
||||
// const [ projectDetail , setProjectDetail ]= useState(undefined);
|
||||
const [ inviteCode , setInviteCode ] = useState(undefined);
|
||||
let { platform , mirror_status , bannerList , projectDetail, projectEntries, projectReadMe } = props ;
|
||||
|
||||
projectDetail = props.defaultDetail || projectDetail || {}
|
||||
|
||||
const [ inviteCode , setInviteCode ] = useState(projectDetail.invite_code || undefined);
|
||||
const [ treeValue , setTreeValue ] = useState(undefined);
|
||||
const [ treeValuePath , setTreeValuePath ] = useState(undefined);
|
||||
const [ lastCommit,setLastCommit ] = useState(undefined);
|
||||
const [ lastCommitAuthor,setLastCommitAuthor ] = useState(undefined);
|
||||
const [ lastCommit,setLastCommit ] = useState((projectEntries.last_commit && projectEntries.last_commit.commit) || undefined);
|
||||
const [ lastCommitAuthor,setLastCommitAuthor ] = useState((projectEntries.last_commit && projectEntries.last_commit.author) || undefined);
|
||||
const [ type ,setType ] = useState('dir');
|
||||
const [ hide , setHide ] = useState(true);
|
||||
const [ hideBtn , setHideBtn ] = useState(false);
|
||||
const [ commitCount ,setCommitCount ] = useState(0);
|
||||
const [ dirInfo ,setDirInfo ] = useState(undefined);//文件夹目录列表
|
||||
const [ commitCount ,setCommitCount ] = useState(projectEntries.commits_count || 0);
|
||||
const [ dirInfo ,setDirInfo ] = useState(projectEntries.entries || undefined);//文件夹目录列表
|
||||
const [ fileInfo ,setFileInfo ] = useState(undefined);//文件内容信息
|
||||
const [ zip_url , setZip_url ] = useState(undefined);
|
||||
const [ tar_url , setTar_url ] = useState(undefined);
|
||||
const [ zip_url , setZip_url ] = useState(projectEntries.zip_url || undefined);
|
||||
const [ tar_url , setTar_url ] = useState(projectEntries.tar_url || undefined);
|
||||
const [ readOnly , setReadOnly] = useState(undefined);
|
||||
const [ isSpin , setIsSpin] = useState(false);
|
||||
const [ visible ,setVisible ] = useState(false);
|
||||
const [ mainFlag ,setMainFlag ] = useState(false);
|
||||
const [ openModal , setOpenModal ] = useState(false);
|
||||
const [ desc , setDesc ] = useState(undefined);
|
||||
const [ website , setWebsite ] = useState(undefined);
|
||||
const [ lesson_url , setLessonUrl ] = useState(undefined);
|
||||
const [ topics, setTopics] = useState(undefined);
|
||||
const [ readme , setReadme ] = useState(undefined);
|
||||
const [ defaultBranch , setDefaultBranch ] = useState(undefined);
|
||||
const [ desc , setDesc ] = useState(projectDetail.description || undefined);
|
||||
const [ website , setWebsite ] = useState(projectDetail.website ||undefined);
|
||||
const [ lesson_url , setLessonUrl ] = useState(projectDetail.lesson_url || undefined);
|
||||
const [ topics, setTopics] = useState(projectDetail.topics || undefined);
|
||||
const [ readme , setReadme ] = useState(projectReadMe || undefined);
|
||||
const [ defaultBranch , setDefaultBranch ] = useState(projectDetail.default_branch || undefined);
|
||||
const [ editReadme , setEditReadme ] = useState(false);
|
||||
const [ pullsFlag , setPullsFlag ] = useState(true);
|
||||
const [ issuesFlag , setIssuesFlag ] = useState(true);
|
||||
|
|
@ -63,7 +68,6 @@ function CoderDepot(props){
|
|||
branchName = returnbar(branchName);
|
||||
let pathname = props.history.location.pathname;
|
||||
let search = props.history.location.search
|
||||
const { platform , mirror_status , bannerList , projectDetail } = props ;
|
||||
//distribution:判断此用户是否可以创建发行版
|
||||
const distribution = projectDetail && projectDetail.type ===0 && ( projectDetail.permission && projectDetail.permission !== "Reporter");
|
||||
|
||||
|
|
@ -109,9 +113,9 @@ function CoderDepot(props){
|
|||
const { author, name, description, default_branch} = projectDetail;
|
||||
if(branchName && branchName !== default_branch){
|
||||
// 处于某分支/标签
|
||||
document.title = `${author.name}/${name}-${branchName}-for gitlink;for git`;
|
||||
document.title = `${author && author.name}/${name}-${branchName}-for gitlink;for git`;
|
||||
}else{
|
||||
document.title = `${author.name}/${name}${description?': '+description:''}-for gitlink;for git` ;
|
||||
document.title = `${author && author.name}/${name}${description?': '+description:''}-for gitlink;for git` ;
|
||||
}
|
||||
}
|
||||
}, [treeValuePath, projectDetail, branchName])
|
||||
|
|
@ -134,7 +138,9 @@ function CoderDepot(props){
|
|||
setType("file");
|
||||
}else{
|
||||
setTreeValue(undefined);
|
||||
getDirInfo(branchName || defaultBranch);
|
||||
if (!projectEntries) {
|
||||
getDirInfo(branchName || defaultBranch);
|
||||
}
|
||||
setType("dir");
|
||||
}
|
||||
}
|
||||
|
|
@ -142,8 +148,8 @@ function CoderDepot(props){
|
|||
|
||||
// 编辑完回来重新获取
|
||||
useEffect(()=>{
|
||||
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 +571,18 @@ function CoderDepot(props){
|
|||
})
|
||||
}
|
||||
{
|
||||
fileInfo &&
|
||||
<CoderRootFileDetail
|
||||
{...props}
|
||||
detail={fileInfo}
|
||||
readOnly={readOnly}
|
||||
md={mdFlag}
|
||||
onEdit={onEdit}
|
||||
currentBranch={branchName || defaultBranch}
|
||||
branch={branchName || defaultBranch}
|
||||
type={projectDetail.type}
|
||||
treeValuePath={treeValuePath}
|
||||
></CoderRootFileDetail>
|
||||
// fileInfo &&
|
||||
// <CoderRootFileDetail
|
||||
// {...props}
|
||||
// detail={fileInfo}
|
||||
// readOnly={readOnly}
|
||||
// md={mdFlag}
|
||||
// onEdit={onEdit}
|
||||
// currentBranch={branchName || defaultBranch}
|
||||
// branch={branchName || defaultBranch}
|
||||
// type={projectDetail.type}
|
||||
// treeValuePath={treeValuePath}
|
||||
// ></CoderRootFileDetail>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
|
|
@ -671,4 +677,20 @@ function CoderDepot(props){
|
|||
</WhiteBack>
|
||||
)
|
||||
}
|
||||
export default CoderDepot;
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
const {
|
||||
defaultDetail,
|
||||
projectEntries,
|
||||
projectReadMe
|
||||
} = state.projectReducer;
|
||||
return {
|
||||
defaultDetail,
|
||||
projectEntries,
|
||||
projectReadMe
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps
|
||||
)(CoderDepot);
|
||||
|
|
@ -8,7 +8,7 @@ const $ = window.$;
|
|||
|
||||
function CoderDepotReadme({ operate , history , readme , ChangeFile }){
|
||||
const [ menuList ,setMenuList ] = useState(undefined);
|
||||
const [ content ,setContent ] = useState(undefined);
|
||||
const [ content ,setContent ] = useState( (readme && readme.replace_content) || undefined );
|
||||
|
||||
useEffect(()=>{
|
||||
if(readme && readme.replace_content){
|
||||
|
|
|
|||
|
|
@ -2,21 +2,26 @@ 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 actions from '../../redux/actions';
|
||||
import types from '../../redux/actions/actionTypes'
|
||||
import { getProjectFunc, getProjectDetailFunc } from '../../services/project'
|
||||
import '../css/index.scss'
|
||||
import './list.scss';
|
||||
|
||||
import { ImageLayerOfCommentHOC } from "../../modules/page/layers/ImageLayerOfCommentHOC";
|
||||
|
||||
|
||||
import Loadable from 'react-loadable';
|
||||
import Loading from '../../Loading';
|
||||
|
||||
import axios from 'axios';
|
||||
import { async } from 'q';
|
||||
|
||||
const Setting = Loadable({
|
||||
loader: () => import('../Settings/Index'),
|
||||
|
|
@ -90,10 +95,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'),
|
||||
|
|
@ -165,11 +170,12 @@ function checkPathname(projectsId, owner, pathname) {
|
|||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
class Detail extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
projectDetail: undefined,
|
||||
projectDetail: props.defaultDetail || undefined,
|
||||
isManager: false,
|
||||
isReporter: false,
|
||||
isDeveloper: false,
|
||||
|
|
@ -182,7 +188,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,
|
||||
|
|
@ -192,22 +198,33 @@ 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);
|
||||
if (this.props.clearProject) {
|
||||
const typeList = [ types.GET_PROJECT_BASE, types.GET_PROJECT_DETAIL, types.GET_PROJECT_ENTRIES, types.GET_PROJECT_MENU, types.GET_PROJECT_README ]
|
||||
typeList.forEach(e => {
|
||||
this.props.clearProject(e)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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) => {
|
||||
|
|
@ -225,56 +242,71 @@ 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);
|
||||
if (data.data) {
|
||||
this.getProject(data.data)
|
||||
}
|
||||
} else {
|
||||
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
|
||||
})
|
||||
this.getBanner(this.props.projectMenu)
|
||||
let projectdata
|
||||
if (this.props.defaultDetail) {
|
||||
projectdata = this.props.defaultDetail
|
||||
} else {
|
||||
projectdata = await getProjectDetailFunc(owner, projectsId, !window.location.host).data;
|
||||
}
|
||||
if (projectdata) {
|
||||
this.getDetail(projectdata)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 工作流激活后修改状态
|
||||
|
|
@ -358,58 +390,67 @@ 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) => { })
|
||||
}
|
||||
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
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 关注和取消关注
|
||||
|
|
@ -879,7 +920,42 @@ class Detail extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
export default withRouter(ImageLayerOfCommentHOC({
|
||||
imgSelector: ".imageLayerParent img, .imageLayerParent .imageTarget",
|
||||
parentSelector: ".newContainer",
|
||||
})(Detail));
|
||||
const mapStateToProps = (state) => {
|
||||
const {
|
||||
projectBase,
|
||||
defaultDetail,
|
||||
projectMenu,
|
||||
} = state.projectReducer;
|
||||
return {
|
||||
projectBase,
|
||||
defaultDetail,
|
||||
projectMenu,
|
||||
};
|
||||
}
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
clearProject: (type) => dispatch(actions.clearProject(type)),
|
||||
});
|
||||
|
||||
Detail.preFetch = ({ store, match, query }) => {
|
||||
return new Promise(async function(resolve, reject) {
|
||||
let { dispatch, getState } = store;
|
||||
// const { owner,projectsId } = 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,
|
||||
mapDispatchToProps
|
||||
)(Detail));
|
||||
|
|
@ -28,16 +28,16 @@ class IndexItem extends Component {
|
|||
<img className="p-r-photo" alt="" src={item.author && item.author.image_url} ></img>
|
||||
</a>
|
||||
:
|
||||
<Link to={`/${item.author && item.author.login}`} className="show-user-link">
|
||||
<a href="javascript:void(0)" onClick={() => { window.location.href = `/${item.author && item.author.login}/${item.identifier}` }} className="show-user-link">
|
||||
<img className="p-r-photo" alt="" src={getImageUrl(`/${item.author && item.author.image_url}`)} ></img>
|
||||
</Link>
|
||||
</a>
|
||||
}
|
||||
<div className="p-r-Infos">
|
||||
<div className="p-r-name">
|
||||
<AlignCenter>
|
||||
<Link to={`/${item.author.login}/${item.identifier}`} title={`${item.author.name}/${item.name}`} className="color-grey-3 font-18 task-hide " style={{maxWidth: 470 }}>
|
||||
<a href="javascript:void(0)" onClick={() => { window.location.href = `/${item.author && item.author.login}/${item.identifier}` }} title={`${item.author.name}/${item.name}`} className="color-grey-3 font-18 task-hide " style={{maxWidth: 470 }}>
|
||||
{item.author.name}/{item.name}
|
||||
</Link>
|
||||
</a>
|
||||
{ !item.is_public && <span className="privateTag">私有</span> }
|
||||
{
|
||||
item.forked_from_project_id ?
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ function Index() {
|
|||
}
|
||||
<div className="itemTitle">
|
||||
<div className="item-title-infos">
|
||||
<Link to={`/${i.author && i.author.login}/${i.identifier}`} className="infotitle task-hide">{i.author && i.author.name}/{i.name}</Link>
|
||||
<a href="javascript:void(0)" onClick={() => { window.location.href = `/${i.author && i.author.login}/${i.identifier}` }} className="infotitle task-hide">{i.author && i.author.name}/{i.name}</a>
|
||||
{i.praises_count > 0 ? <span><i className="iconfont icon-xingzhuang mr3 font-14"></i>{i.praises_count}</span> :"" }
|
||||
{i.forked_count > 0 ? <span><i className="iconfont icon-yifuke_icon mr3 font-14"></i>{i.forked_count}</span>:""}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ const ProjectDetail = Loadable({
|
|||
loader: () => import("../Main/Detail"),
|
||||
loading: Loading,
|
||||
});
|
||||
export default withRouter(CNotificationHOC()(SnackbarHOC()(TPMIndexHOC(
|
||||
const team = CNotificationHOC()(SnackbarHOC()(TPMIndexHOC(
|
||||
((props)=>{
|
||||
return (
|
||||
<div className="newMain">
|
||||
|
|
@ -114,4 +114,8 @@ export default withRouter(CNotificationHOC()(SnackbarHOC()(TPMIndexHOC(
|
|||
</div>
|
||||
)
|
||||
})
|
||||
))))
|
||||
)))
|
||||
|
||||
team.preFetch = ProjectDetail.preFetch
|
||||
|
||||
export default withRouter(team)
|
||||
|
|
@ -87,7 +87,7 @@ function ConcentrateProject({
|
|||
list.map((i,k)=>{
|
||||
return(
|
||||
<li key={i.id}>
|
||||
<Link to={`/${i.author && i.author.login}/${i.identifier}`} className="name task-hide">{i.name}</Link>
|
||||
<a href="javascript:void(0)" onClick={() => { window.location.href = `/${i.author && i.author.login}/${i.identifier}`}}className="name task-hide">{i.name}</a>
|
||||
<p className="task-hide desc">{i.description}</p>
|
||||
{/* 项目标签 */}
|
||||
{i.topics && <div className='viewProListTopics mt10'>
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
@ -15,32 +15,89 @@ const ProjectDetail = Loadable({
|
|||
loader: () => import("../Main/Detail"),
|
||||
loading: Loading,
|
||||
});
|
||||
export default withRouter(
|
||||
(CNotificationHOC()(SnackbarHOC()(TPMIndexHOC((props) => {
|
||||
|
||||
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 (
|
||||
<Switch>
|
||||
|
||||
{secondRouter && (!userRouterArr.includes(secondRouter)) ? <Route
|
||||
path="/:owner/:projectsId"
|
||||
render={(p) => (
|
||||
<ProjectDetail {...props} {...p} />
|
||||
<ProjectDetail {...this.props} {...p} />
|
||||
)}
|
||||
></Route> : <Route
|
||||
path="/:username"
|
||||
render={(p) => (
|
||||
<Infos {...props} {...p} />
|
||||
<Infos {...this.props} {...p} />
|
||||
)}
|
||||
></Route>}
|
||||
|
||||
</Switch>
|
||||
)
|
||||
}))))
|
||||
)
|
||||
}
|
||||
}
|
||||
const user = (CNotificationHOC()(SnackbarHOC()(TPMIndexHOC(DetailTop))))
|
||||
|
||||
user.preFetch = ProjectDetail.preFetch
|
||||
|
||||
export default withRouter(user)
|
||||
13
src/index.js
13
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(
|
||||
<AppContainer >
|
||||
ReactDOM.hydrate(
|
||||
<AppContainer>
|
||||
{/* <Component /> */}
|
||||
<Provider store={store}>
|
||||
|
||||
<BrowserRouter basename='/'>
|
||||
<Route path={`/`} component={App}></Route>
|
||||
<Route path='/' component={App}></Route>
|
||||
</BrowserRouter>
|
||||
</Provider>
|
||||
|
||||
</AppContainer>,
|
||||
document.getElementById('root')
|
||||
)
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@
|
|||
* @LastEditors: tangjiang
|
||||
* @LastEditTime: 2019-12-02 16:33:35
|
||||
*/
|
||||
import 'quill/dist/quill.core.css';
|
||||
import 'quill/dist/quill.bubble.css';
|
||||
import 'quill/dist/quill.snow.css';
|
||||
// import 'quill/dist/quill.core.css';
|
||||
// import 'quill/dist/quill.bubble.css';
|
||||
// import 'quill/dist/quill.snow.css';
|
||||
import './index.scss';
|
||||
import React, { useState, useImperativeHandle, useRef, useEffect } from 'react';
|
||||
import { Form, Input, InputNumber, Button, Select } from 'antd';
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
// import 'quill/dist/quill.snow.css';
|
||||
// import 'katex/dist/katex.css';
|
||||
import './index.scss';
|
||||
import 'katex/dist/katex.min.css';
|
||||
// import 'katex/dist/katex.min.css';
|
||||
import React from 'react';
|
||||
import katex from 'katex';
|
||||
const Quill = require('quill');
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ export function TPMIndexHOC(WrappedComponent) {
|
|||
return class II extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
window.$('#root').css('position', 'relative');
|
||||
// window.$('#root').css('position', 'relative');
|
||||
|
||||
this.state = {
|
||||
tpmLoading: true,
|
||||
|
|
@ -61,7 +61,13 @@ export function TPMIndexHOC(WrappedComponent) {
|
|||
window.removeEventListener('keyup', this.keyupListener)
|
||||
}
|
||||
componentWillMount() {
|
||||
this.fetchUsers();
|
||||
if (!__SERVER__) {
|
||||
this.fetchUsers();
|
||||
} else {
|
||||
this.setState({
|
||||
current_user: {}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -126,6 +126,16 @@ import {
|
|||
changeWXCodeEvaluateLoading,
|
||||
changeWXCodeEvaluateDialog
|
||||
} from './wxCode';
|
||||
|
||||
import {
|
||||
setProject,
|
||||
clearProject,
|
||||
setProjectDetail,
|
||||
setProjectEntries,
|
||||
setProjectMenuList,
|
||||
setProjectReadMe
|
||||
} from './server.js';
|
||||
|
||||
export default {
|
||||
toggleTodo,
|
||||
getOJList,
|
||||
|
|
@ -223,5 +233,12 @@ export default {
|
|||
evaluateWxCode,
|
||||
showWXCodeTextCase,
|
||||
changeWXCodeEvaluateLoading,
|
||||
changeWXCodeEvaluateDialog
|
||||
changeWXCodeEvaluateDialog,
|
||||
// 服务端渲染
|
||||
setProject,
|
||||
clearProject,
|
||||
setProjectDetail,
|
||||
setProjectEntries,
|
||||
setProjectMenuList,
|
||||
setProjectReadMe
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
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 clearProject = (type) => {
|
||||
return {
|
||||
type: type,
|
||||
payload: ''
|
||||
}
|
||||
}
|
||||
|
||||
// 获取项目详情信息
|
||||
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 => {
|
||||
if (res && res.data) {
|
||||
dispatch({
|
||||
type: types.GET_PROJECT_README,
|
||||
payload: res.data
|
||||
});
|
||||
return res.data
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
import axios from 'axios';
|
||||
import { url } from '../../config/ssrUrl';
|
||||
|
||||
const baseUrl = `${url}/api`
|
||||
|
||||
//
|
||||
export async function getProjectFunc (owner, projectsId) {
|
||||
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 }});
|
||||
}
|
||||
Loading…
Reference in New Issue