gitlink ssr 动态head版本

This commit is contained in:
黄心宇 2024-04-16 09:21:49 +08:00
parent 7d5922ca49
commit 2c3615b628
13 changed files with 125 additions and 66 deletions

View File

@ -1,8 +1,8 @@
import app from './src/App'
import users from './src/forge/users/Index';
import Detail from "./src/forge/Main/Detail";
import Information from "./src/forge/Information/index"
import NewsDetail from './src/forge/Information/Pages/newsDetail';
// import users from './src/forge/users/Index';
// import Detail from "./src/forge/Main/Detail";
// import Information from "./src/forge/Information/index"
// import NewsDetail from './src/forge/Information/Pages/newsDetail';
export default [
{
@ -26,16 +26,16 @@ export const deepRoutes = [
{
key: "zoneNews",
path: "/zone/:deptId/newdetail/:id",
component: NewsDetail
// component: NewsDetail
},
{
key: "zone",
path: "/zone/:deptId",
component: Information
// component: Information
},
{
key: "detail",
path: "/:owner/:projectsId",
component: Detail,
// component: Detail,
}
]

View File

@ -17,6 +17,13 @@ const paths = require("./paths");
const publicPath = "/";
const env = getClientEnvironment("/","development");
const process = require('process');
process.on('uncaughtException', function (err) {
// 开发时websocket proxy 会因服务重新部署而报错中断本地 server这里拦截一下
console.log('拦截未处理异常:', err);
});
module.exports = {
optimization: {
splitChunks: {

17
package-lock.json generated
View File

@ -235,7 +235,7 @@
},
"@babel/helper-annotate-as-pure": {
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz",
"resolved": "https://registry.npmmirror.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz",
"integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==",
"requires": {
"@babel/types": "^7.22.5"
@ -243,7 +243,7 @@
},
"@babel/helper-builder-binary-assignment-operator-visitor": {
"version": "7.22.15",
"resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz",
"resolved": "https://registry.npmmirror.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz",
"integrity": "sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==",
"requires": {
"@babel/types": "^7.22.15"
@ -1456,7 +1456,7 @@
},
"core-js": {
"version": "2.6.12",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
"resolved": "https://registry.npmmirror.com/core-js/-/core-js-2.6.12.tgz",
"integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==",
"dev": true
},
@ -1480,7 +1480,7 @@
},
"regenerator-runtime": {
"version": "0.11.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
"resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==",
"dev": true
}
@ -1497,7 +1497,7 @@
"dependencies": {
"core-js": {
"version": "2.6.12",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
"resolved": "https://registry.npmmirror.com/core-js/-/core-js-2.6.12.tgz",
"integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ=="
}
}
@ -21603,15 +21603,13 @@
"version": "2.1.1",
"resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"dev": true,
"optional": true
"dev": true
},
"is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dev": true,
"optional": true,
"requires": {
"is-extglob": "^2.1.1"
}
@ -21620,8 +21618,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz",
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
"dev": true,
"optional": true
"dev": true
},
"readdirp": {
"version": "3.6.0",

View File

@ -157,8 +157,8 @@
"analyze": "npm run build -- --stats && webpack-bundle-analyzer build/bundle-stats.json",
"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",
"dev:server": "cross-env NODE_ENV=dev nodemon --exec babel-node \"./buildserver/bundle.js\" --watch config --watch server",
"dev:build:server": "cross-env NODE_ENV=dev webpack --config config/webpack.server.js",
"pm2": "pm2 start pm2.json"
},
"jest": {

View File

@ -10,11 +10,12 @@ import App from "../src/App";
import { Route } from 'react-router-dom';
import { setDefaultMeta } from '../src/common/UrlTool'
import Logger from "./log";
import { changeHead } from './setHead'
export const render = async (req,res)=>{
let _route = null,
_match = null;
Logger.info(`${req.url} UA:${req.headers['user-agent']}`)
// Logger.info(`${req.url} UA:${req.headers['user-agent']}`)
let store = serverStore();
@ -57,39 +58,47 @@ export const render = async (req,res)=>{
query: req.path,
});
}
const content = renderToString((
<Provider store={store}>
<StaticRouter location={req.path} context={context}>
{renderRoutes(Routes)}
{/* <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);
if (_route) {
await changeHead(_route, _match.params)
}
// 暂时不开放ssr只修改head
// const content = renderToString((
// <Provider store={store}>
// <StaticRouter location={req.path} context={context}>
// {renderRoutes(Routes)}
// {/* <Route path='/:owner/:projectsId' exact component={App}></Route> */}
// </StaticRouter>
// </Provider>
// ))
// 暂时不开放ssr只修改head
// 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>`);
// 暂时不开放ssr只修改head
// 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))
// 暂时不开放ssr只修改head
// res.send(prepHTML(html, content))
res.send(prepHTML(html, ''))
store = null
}

35
server/setHead.js Normal file
View File

@ -0,0 +1,35 @@
// 兜底方案先改head
import { setDefaultMeta, setSeoMeta } from '../src/common/UrlTool'
import { getProjectDetailFunc, getMainInfos, getNewsDetail } from '../src/services/project'
export const changeHead = async ({ key }, params) => {
let res
let data
switch (key) {
case 'detail':
res = await getProjectDetailFunc(params.owner, params.projectsId);
data = res.data
if (data && data.status !== 404) {
let keyWords=`${params.owner},${params.projectsId},${data.author && data.author.name}`;
let title= `${params.owner}/${params.projectsId}${data.description?''+data.description:''}`;
setSeoMeta(keyWords,title,data.description || '',`/${params.owner}/${params.projectsId}`,params.owner,params.projectsId);
}
break;
case 'zoneNews':
res = await getNewsDetail(params.id)
data = res.data.data
if (data) {
setSeoMeta(`${data.name}`, data.name, data.subTitle || '', `/zone/${params.deptId}/newdetail/${ params.id }`)
}
break;
case 'zone':
res = await getMainInfos(params.deptId);
data = res.data.data
if (data) {
setSeoMeta(`${data.name}`, data.name, data.subTitle || '', `/zone/${params.deptId}`)
}
break;
default:
break;
}
}

View File

@ -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) {

View File

@ -360,7 +360,7 @@ export function setSeoMeta(keyWords, title, description, url, owner, projectId)
document.querySelector(`meta[property='og:url']`).content = origin + url;
document.querySelector(`meta[property='og:description']`).content = description + ' | GitLink';
document.querySelector(`meta[property='og:image:alt']`).content = description + ' | GitLink';
document.querySelector('meta[name="Keywords"]').content= `${ keyWords },${ keyWords ? '' : 'GitLink, git, 开源, 代码托管, 项目管理, 版本控制, 代码分享, 项目协作, 协同开发平台' }` ;
document.querySelector('meta[name="Keywords"]').content= `${ keyWords }${ keyWords ? '' : 'GitLink, git, 开源, 代码托管, 项目管理, 版本控制, 代码分享, 项目协作, 协同开发平台' }` ;
document.querySelector(`meta[name='description']`).content = description + ' | GitLink';
document.querySelector(`meta[name='go-import']`).content = host + url + ' git ' + origin + url;
document.querySelector(`meta[name='octolytics-dimension-user_login']`).content = owner;

View File

@ -8,7 +8,7 @@ import { isPhone } from 'educoder';
import { tempConfig } from '../tempInfo';
import Acce from '../Component/mobile/accessory';
import { addMeta, setSeoMeta } from 'educoder';
// import CommentList from '../Component/comments/list'
import CommentList from '../Component/comments/list'
import { connect } from 'react-redux';
import { withRouter } from "react-router";
import { setZoneDetail, setNewsDetail } from '../../../redux/actions/server';
@ -23,9 +23,6 @@ function NewsDetail(props){
const [ commentReload, setCommentReload] = useState(undefined);
const temp = props.temp;
const zonedetail = props.zoneDetail || props.data;
if (__SERVER__ && detail) {
setMeta()
}
useEffect(()=>{
let initialContent = undefined;
@ -50,7 +47,7 @@ function NewsDetail(props){
}
if (zonedetail) {
document.title= zonedetail.mainTitle;
setSeoMeta(`${zonedetail.name},`, zonedetail.name, zonedetail.subTitle, `/zone/${deptId}`)
setSeoMeta(`${zonedetail.name},`, zonedetail.name, zonedetail.subTitle, `/zone/${deptId}/newdetail/${ id }`)
}
}
},[zonedetail,detail])

View File

@ -109,7 +109,7 @@ function CoderDepot(props){
useEffect(()=>{
//
if(projectDetail){
if(projectDetail && projectDetail.project_identifier){
const { author, name, description, default_branch} = projectDetail;
if(branchName && branchName !== default_branch){
// /

View File

@ -310,7 +310,10 @@ class Detail extends Component {
if (this.props.defaultDetail) {
projectdata = this.props.defaultDetail
} else {
projectdata = await getProjectDetailFunc(owner, projectsId, !window.location.host).data;
const res = await getProjectDetailFunc(owner, projectsId, !window.location.host);
if (res.status === 200) {
projectdata = res.data
}
}
if (projectdata) {
this.getDetail(projectdata)

View File

@ -1,8 +1,15 @@
import axios from 'axios';
import { url, zoneUrl } from '../ssrUrl';
const baseUrl = `${url}/api`
const baseZoneUrl = `${zoneUrl}/api`
let baseUrl = `${url}/api`
let baseZoneUrl = `${zoneUrl}/api`
if (__CLIENT__) {
let settings = localStorage.chromesetting&&JSON.parse(localStorage.chromesetting);
baseUrl = settings && settings.common.main_site_url + '/api';
baseZoneUrl = settings && settings.common.zone + '/api';
}
//
export async function getProjectFunc (owner, projectsId) {

View File

@ -1,3 +1,7 @@
export const url = process.env.NODE_ENV === 'production' ? 'https://www.gitlink.org.cn' : 'http://172.20.32.202:4000'
export const zoneUrl = process.env.NODE_ENV === 'production' ? 'https://gateway.gitlink.org.cn' : 'http://172.20.32.202:4000'
export const host = process.env.NODE_ENV === 'production' ? 'www.gitlink.org.cn' : '172.20.32.202:4000'
export const url = process.env.NODE_ENV === 'production' ? 'https://www.gitlink.org.cn' : 'https://testforgeplus.trustie.net'
export const zoneUrl = process.env.NODE_ENV === 'production' ? 'https://gateway.gitlink.org.cn' : 'https://testgetway.trustie.net'
export const host = process.env.NODE_ENV === 'production' ? 'www.gitlink.org.cn' : 'testforgeplus.trustie.net'
// export const url = process.env.NODE_ENV === 'production' ? 'https://www.gitlink.org.cn' : ''
// export const zoneUrl = process.env.NODE_ENV === 'production' ? 'https://gateway.gitlink.org.cn' : ''
// export const host = process.env.NODE_ENV === 'production' ? 'www.gitlink.org.cn' : ''