Giter Site home page Giter Site logo

fantasticit / wipi Goto Github PK

View Code? Open in Web Editor NEW
612.0 9.0 121.0 13.08 MB

A blog system written by next.js, nest.js and MySQL.

Home Page: https://blog.codingit.cn/

License: Apache License 2.0

JavaScript 2.69% Shell 0.24% TypeScript 84.31% SCSS 12.68% Less 0.07%
react sass nextjs nestjs nysql

wipi's Introduction

Wipi

简介

Wipi 是一个面向个人的开源的集成文章发表、页面创建、知识小册等功能的 CMS 系统。涉及到的技术如下:

  • MySQL:数据存储
  • next.js:前端页面框架
  • nest.js:服务端框架
  • AliyunOSS:对象存储

链接

功能点

  • 文章管理
  • 页面管理
  • 知识小册
  • 评论管理
  • 邮件管理
  • 访问统计
  • 文件管理
  • 系统设置

更多功能,欢迎访问系统进行体验。

预览

  • 文章列表
  • 文章详情
  • 动态页面
  • 知识小册
  • 后台管理
  • 文章编辑
  • 小册编辑

项目运行

数据库

首先安装 MySQL,推荐使用 docker 进行安装。

docker image pull mysql:5.7
docker run -d --restart=always --name wipi -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root mysql:5.7

然后在 MySQL 中创建数据库。

docker container exec -it wipi bash;
mysql -u root -p;
CREATE DATABASE  `wipi` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

本地运行

首先,clone 项目。

git clone --depth=1 https://github.com/fantasticit/wipi.git your-project-name

然后,安装项目依赖。

# 全局安装 pnpm
npm i -g pnpm

pnpm install
  • 启动项目
pnpm run dev

前台页面地址:http://localhost:3001。 后台管理地址:http://localhost:3002。 服务接口地址:http://localhost:3003

首次启动,默认创建管理员用户:admin,密码:admin(可在 .env 文件中进行修改)。 [PS] 如服务端配置启动失败,请先确认 MySQL 的配置是否正确,配置文件在 .env

系统设置

  • 系统设置
  • 前台页面

项目初次启动时,需要在后台进行系统设置。随着内容的丰富,页面内容也会丰富起来。

配置文件

默认加载 .env 文件,生产环境会尝试加载 .env.prod 文件。

# 客户端运行端口
CLIENT_PORT=3001
# 客户端站点地址(假设部署到 https://xx.com, 就将 CLIENT_SITE_URL 设置为 https://xx.com)
CLIENT_SITE_URL=http://localhost:3001
# 客户端资源地址(假设部署到 https://xx.com,就将 CLIENT_ASSET_PREFIX 设置为 https://xx.com,如果将资源上传到 cdn ,那就改为 cdn 地址)
CLIENT_ASSET_PREFIX=/

# 管理后台运行端口
ADMIN_PORT=3002
# 管理后台资源地址(假设部署到 https://xx.com,就将 CLIENT_ASSET_PREFIX 设置为 https://xx.com,如果将资源上传到 cdn ,那就改为 cdn 地址)
ADMIN_ASSET_PREFIX=/

# 服务端运行端口
SERVER_PORT=3003
# 服务端完整访问路径
SERVER_API_URL=http://localhost:3003/api
# 服务端接口前缀(假设将希望通过 http://xx:com/api 来访问,那就设置为 /api;如果 http://xx:com,那就设置为 / )
SERVER_API_PREFIX=/api
# 默认管理员账户名
ADMIN_USER=admin
# 默认管理员账密码
ADMIN_PASSWD=admin
# 以下为数据库配置,请先创建好表
DB_HOST=127.0.0.1
DB_PORT=3306
DB_USER=root
DB_PASSWD=root
DB_DATABASE=wipi

# Github 第三方登录配置
# 关于 Github OAuth 可参考 https://www.ruanyifeng.com/blog/2019/04/github-oauth.html
GITHUB_CLIENT_ID=0 # Github OAuth 登录 Id
GITHUB_CLIENT_SECRET=0 # Github OAuth 登录 Secret

项目部署

生产环境部署的脚本如下:

node -v
npm -v

npm config set registry http://registry.npmjs.org

npm i -g pm2 @nestjs/cli pnpm

pnpm install
pnpm run build
pnpm run pm2

pm2 startup
pm2 save

nginx 配置

采用反向代理进行 nginx 配置,同时设置 proxy_set_header X-Real-IP $remote_addr; 以便服务端获取到真实 ip 地址

upstream wipi_client {
  server 127.0.0.1:3000;
  keepalive 64;
}

# http -> https 重定向
server {
  listen  80;
  server_name 域名;
  rewrite ^(.*)$  https://$host$1 permanent;
}

server {
  listen 443 ssl;
  server_name 域名;
  ssl_certificate      证书存放路径;
  ssl_certificate_key  证书存放路径;

  location / {
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    proxy_set_header X-Nginx-Proxy true;
    proxy_cache_bypass $http_upgrade;
    proxy_pass http://wipi_client; #反向代理
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  }
}

资料

wipi's People

Contributors

fantasticit avatar unrilwever avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

wipi's Issues

Refactor: _app.tsx

import React from 'react';
import App from 'next/app';
import Router from 'next/router';
import { IntlMessages, NextIntlProvider } from 'next-intl';
import { IGlobalContext, GlobalContext } from '@/context/global';
import { SettingProvider } from '@/providers/setting';
import { PageProvider } from '@/providers/page';
import { CategoryProvider } from '@/providers/category';
import { TagProvider } from '@/providers/tag';
import { AppLayout } from '@/layout/AppLayout';
import { NProgress } from '@components/NProgress';
import { FixAntdStyleTransition } from '@/components/FixAntdStyleTransition';
import { ViewStatistics } from '@/components/ViewStatistics';
import { Analytics } from '@/components/Analytics';
import 'highlight.js/styles/atom-one-light.css';
import 'viewerjs/dist/viewer.css';
import '@/theme/antd.less';
import '@/theme/reset.scss';
import '@/theme/markdown.scss';

Router.events.on('routeChangeComplete', () => {
  setTimeout(() => {
    window.scrollTo(0, 0);
  }, 0);
});

class MyApp extends App<IGlobalContext, unknown> {
  state = {
    locale: '',
  };

  static async getInitialProps({ Component, ctx }) {
    const getPagePropsPromise = Component.getInitialProps
      ? Component.getInitialProps(ctx)
      : Promise.resolve({});
    const [pageProps, setting, tags, categories, pages] = await Promise.all([
      getPagePropsPromise,
      SettingProvider.getSetting(),
      TagProvider.getTags({ articleStatus: 'publish' }),
      CategoryProvider.getCategory({ articleStatus: 'publish' }),
      PageProvider.getAllPublisedPages(),
    ]);
    const i18n = (() => {
      try {
        return setting.i18n && typeof setting.i18n === 'object'
          ? setting.i18n
          : JSON.parse(setting.i18n as string);
      } catch (e) {
        return {};
      }
    })();
    return {
      pageProps,
      setting,
      tags,
      categories,
      pages: pages[0] || [],
      i18n,
      locales: Object.keys(i18n),
    };
  }

  changeLocale = (key) => {
    window.localStorage.setItem('locale', key);
    this.setState({ locale: key });
  };

  render() {
    // @ts-ignore
    const { Component, pageProps, i18n, locales, router, ...contextValue } = this.props;
    const locale = this.state.locale || router.locale;
    const { needLayoutFooter = true } = pageProps;
    const message = i18n[locale] || {};

    return (
      <GlobalContext.Provider
        value={{
          ...contextValue,
          i18n,
          locale,
          locales,
          changeLocale: this.changeLocale,
        }}
      >
        <NextIntlProvider messages={message as IntlMessages} locale={locale}>
          <FixAntdStyleTransition />
          <ViewStatistics />
          <Analytics />
          <AppLayout needFooter={needLayoutFooter}>
            <NProgress color={'#ff0064'} />
            <Component {...pageProps} />
          </AppLayout>
        </NextIntlProvider>
      </GlobalContext.Provider>
    );
  }
}

export default MyApp;

yarn dev运行后报错

yarn安装完依赖后,执行yarn dev 运行失败了,打开3000,显示的错误如下所示,能否帮忙看看错误原因以及解决办法
屏幕截图 2021-06-18 151708

更加通用的上传配置

目前限制使用 AliyunOSS,改造方案:

  1. 将 upload 配置完全 json 配置化(在后台系统配置中添加编辑器)
  2. 服务端预存几套上传方案,根据配置动态调用

NewFeature

服务端nest能否集成swagger文档

为什么要server依赖同时安装"bcrypt": "^3.0.7", "bcryptjs": "^2.4.3"

您好,打扰一下,就是我在启动项目前安装依赖的时候报了个错,主要是bcrypt安装的一点问题,我将bcrypt去除,yarn安装成功。bcrypt是c++编写的,bcryptjs是js编写的,功能相同,性能bcrypt会好些,项目里也只是使用了bcryptjs,为何还需要安装bcrypt呢,不是二者存其一即可吗?

Monaco JSON 支持

import * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js';
import 'monaco-editor/esm/vs/language/json/monaco.contribution';
import 'monaco-editor/esm/vs/editor/contrib/format/formatActions';
export default {
    props:{
        value:{
            type: Array,
            default: []
        }
    },
    data(){
        return {
            monacoInstance: null
        }
    },
    mounted: function(){
        const self = this;
        const value = this.value;
        const monacoInstance = monaco.editor.create(document.getElementById("monaco-editor"),{
            value:`${JSON.stringify(value)}`,
            language: "json"
        });
        monacoInstance.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_S, function(){
            console.log('saved')
            const value = monacoInstance.getValue();
            self.updateValue(JSON.parse(value));
        });
        setTimeout(()=>{
            monacoInstance.getAction('editor.action.formatDocument').run();
        },500)
        this.monacoInstance = monacoInstance;
    },
    methods:{
        updateValue: function(value){
            console.log(value);
            this.$emit('updateValue',value);
        }
    },
    beforeDestroy: function(){
        this.monacoInstance.dispose();
    }
}

奇怪的500错误

系统信息

  • Fedora 38 Workstation
  • Node.js 16.15
  • Chrome
  • Vscode

大半年前在本地调试一直都没问题,今天在本地跑突然出了500内部错误,莫非和系统更新有关?
截图 2023-06-08 17-08-21
截图 2023-06-08 17-07-53_mosaic
截图 2023-06-08 17-08-33_mosaic

server 启动报错

node环境为 v15.10.0 时启动报错:
截屏2021-07-02 下午8 45 43
服务端无法正常启动,于是将node环境切到v14.15.1 启动项目,3端都能运行,但仍有报错,admin端新增文章时接口返回500,异常信息如下:
截屏2021-07-02 下午9 16 18
非常感谢大佬的开源项目,希望大佬后期出一期 lerna 搭建大型混合工程的视屏教程,大佬的目录结构清晰明了,非常适合全栈开发学习!~

国际化优先级

本地存储的国际化 key 值,优先级高于了通过诸如 /en/ 域名访问的国际化 key 值。

群晖中yarn安装依赖未成功,请帮忙看看,谢谢。

在群晖中按最新教程尝试安装还是没成功,请帮忙看看如何解决,或者能否给一个Docker Compose安装方法。谢谢。
数据库安装OK

首先安装 MySQL,推荐使用 docker 进行安装。

docker run -d --restart=always --name wipi -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root mysql:5.7
然后在 MySQL 中创建数据库。

docker container exec -it wipi  bash;
mysql -u root -p;
CREATE DATABASE  `wipi` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

Clone 项目OK

首先,clone 项目。
git clone --depth=1 https://github.com/fantasticit/wipi.git your-project-name

安装项目依赖NG

安装项目依赖。
yarn

错误信息

 ERROR: There are no scenarios; must have at least one.

Bug

管理后台不需要登录就能访问dashboard

docker

FROM node:14

RUN apt-get update \
  && apt-get install -y wget gnupg \
  && wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \
  && sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' \
  && apt-get update \
  && apt-get install -y google-chrome-stable fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst fonts-freefont-ttf libxss1 \
    --no-install-recommends \
  && rm -rf /var/lib/apt/lists/*

WORKDIR /app
COPY / /
RUN chmod +x /start.sh


ENV PUPPETEER_SKIP_DOWNLOAD=true

# install global packages
RUN npm install -g yarn pm2 --registry=https://registry.npm.taobao.org --force
RUN npm config set registry https://registry.npm.taobao.org

RUN yarn --unsafe-perm
RUN yarn run build
RUN npm cache clean

EXPOSE 3000
EXPOSE 3001
EXPOSE 4000

CMD ["/start.sh"]
**/node_modules
node_modules
#!/bin/bash

google-chrome-stable
yarn run pm2

version: '3'
services:
  mysql:
    container_name: mysql
    image: 'mysql:5.7'
    restart: always
    privileged: true
    ports:
      - '3306:3306'
    environment:
      POSTGRES_USER: 'root'
      POSTGRES_PASSWORD: 'root'

  app:
    container_name: app
    build: .
    ports:
      - '3000:3000'
      - '3001:3001'
      - '4000:4000'

项目初始化打包

client 可能会进行 pre-render,这样就依赖接口,这样就需要服务先启动,导致 client 打包失败。

谁来升级成 webpack5

这个工程主要是 webpack4 打包,可以用 webpack5 进行升级不?大神帮忙改进下。

config 脚本重构

const fs = require('fs-extra');
const path = require('path');
const dotenv = require('dotenv');
const isProd = process.env.NODE_ENV === 'production';

function parseEnv() {
  const localenv = path.resolve(__dirname, '../.env');
  const prodenv = path.resolve(__dirname, '../.env.prod');

  if (!fs.existsSync(localenv) && !fs.existsSync(prodenv)) {
    throw new Error(`Can not locate any .env file in ${__dirname}`);
  }

  const file = isProd && fs.existsSync(prodenv) ? prodenv : localenv;
  const config = dotenv.parse(fs.readFileSync(file));

  return { file, config };
}

module.exports = parseEnv();
const path = require('path');
const fs = require('fs-extra');

function parseI18n() {
  const localesDir = path.join(__dirname, '../locales');

  if (!fs.existsSync(localesDir)) {
    return { i18n: {}, locales: [], defaultLocale: '' };
  }

  const files = fs.readdirSync(localesDir);
  const messages = files.reduce((i18n, file) => {
    const language = file.replace(path.extname(file), '');
    const json = fs.readJsonSync(path.join(localesDir, file));
    i18n[language] = json;
    return i18n;
  }, {});
  const locales = Object.keys(messages);
  const defaultLocale = 'zh' in messages ? 'zh' : locales[0];

  return { messages, locales, defaultLocale };
}

module.exports = parseI18n;

本地运行疑似next报错

Warning: render(...): It looks like the React-rendered content of this container was removed without using React. This is not supported and will cause errors. Instead, call ReactDOM.unmountComponentAtNode to empty a container.

启动项目后,访问 client 和 admin 报错,表都没进行初始化操作。

[Nest] 8795 - 2021/02/27 下午4:50:31 [ExceptionsHandler] ER_NO_SUCH_TABLE: Table 'wipi.article' doesn't exist +186516ms
QueryFailedError: ER_NO_SUCH_TABLE: Table 'wipi.article' doesn't exist
at new QueryFailedError (/Volumes/DOC/github/wipi/packages/server/node_modules/typeorm/error/QueryFailedError.js:11:28)
at Query. (/Volumes/DOC/github/wipi/packages/server/node_modules/typeorm/driver/mysql/MysqlQueryRunner.js:216:45)
at Query. (/Volumes/DOC/github/wipi/packages/server/node_modules/mysql/lib/Connection.js:526:10)
at Query._callback (/Volumes/DOC/github/wipi/packages/server/node_modules/mysql/lib/Connection.js:488:16)
at Query.Sequence.end (/Volumes/DOC/github/wipi/packages/server/node_modules/mysql/lib/protocol/sequences/Sequence.js:83:24)
at Query.ErrorPacket (/Volumes/DOC/github/wipi/packages/server/node_modules/mysql/lib/protocol/sequences/Query.js:92:8)
at Protocol._parsePacket (/Volumes/DOC/github/wipi/packages/server/node_modules/mysql/lib/protocol/Protocol.js:291:23)
at Parser._parsePacket (/Volumes/DOC/github/wipi/packages/server/node_modules/mysql/lib/protocol/Parser.js:433:10)
at Parser.write (/Volumes/DOC/github/wipi/packages/server/node_modules/mysql/lib/protocol/Parser.js:43:10)
at Protocol.write (/Volumes/DOC/github/wipi/packages/server/node_modules/mysql/lib/protocol/Protocol.js:38:16)

安装报错Unknown database 'root'

在mysql 中创建数据库报错,请帮忙看看如何解决,谢谢。

mysql -u root -p root;

报错:

root@17e8a4d5a384:/# mysql -u root -p root;
Enter password:
ERROR 1049 (42000): Unknown database 'root'

服务端初始化 i18n 代码错误

修复版本:

const target = old && old[0] || {};
if (target && target.i18n)
    return;
target.i18n = JSON.stringify(setting_constant_1.i18n);
await this.settingRepository.save(target);

管理页面按新建文章,尝试保存时就出现错误

管理页面按新建文章,尝试保存时就出现错误。而且我是用dev模式的,不明白为什么还会去读dist里面的file。谢谢了

[Nest] 6716   - 02/23/2021, 12:32:06 AM   [ExceptionsHandler] Cannot read property 'role' of null +24561ms
TypeError: Cannot read property 'role' of null
    at roles.some.role (E:\wipi\packages\server\dist\modules\auth\roles.guard.js:33:58)
    at Array.some (<anonymous>)
    at RolesGuard.canActivate (E:\wipi\packages\server\dist\modules\auth\roles.guard.js:33:31)
    at GuardsConsumer.tryActivate (E:\wipi\packages\server\node_modules\@nestjs\core\guards\guards-consumer.js:14:34)
    at canActivateFn (E:\wipi\packages\server\node_modules\@nestjs\core\router\router-execution-context.js:132:59)
    at E:\wipi\packages\server\node_modules\@nestjs\core\router\router-execution-context.js:41:37
    at E:\wipi\packages\server\node_modules\@nestjs\core\router\router-proxy.js:8:23
    at Layer.handle [as handle_request] (E:\wipi\packages\server\node_modules\express\lib\router\layer.js:95:5)
    at next (E:\wipi\packages\server\node_modules\express\lib\router\route.js:137:13)
    at Route.dispatch (E:\wipi\packages\server\node_modules\express\lib\router\route.js:112:3)

无法连接到数据库

MySQL 的 docker 镜像已经启动了,服务端报错:

[Nest] 9656   - 02/22/2021, 12:02:59 PM   [TypeOrmModule] Unable to connect to the database. Retrying (1)... +18ms
Error: ER_NOT_SUPPORTED_AUTH_MODE: Client does not support authentication protocol requested by server; consider upgrading MySQL client    
    at Handshake.Sequence._packetToError (D:\Project\Temp\wipi\packages\server\node_modules\mysql\lib\protocol\sequences\Sequence.js:47:14)

我查了一下,试了更改 MySQL server 的认证方式:

ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'root';
flush privileges;

很遗憾,还是不行。

请问你这里用到的 MySQL 版本是多少呢?有做其他配置吗?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.