Waline 评论系统折腾记录

开端

最近正在研究夸父的评论系统。国庆之后有过一段调研,当时了解到了 Waline ,但现在已经忘光,所以从零开始继续学习 Waline 的用法。写这篇文章是因为捣鼓 Waline 的时候遇到了太多困难,发一篇牢骚(谁叫自己造轮子太困难了呢。

发现异常

想要直接部署这个服务非常简单,但想要自定义它的一些功能非常困难。虽然官方有说明支持自定义部分功能,但一系列问题还是让我摸不着头脑:

  1. 官方提供的运行方法让自定义看上去不太可能:

    这是在直接运行库内的 JS 文件,难道我要去修改他们的包吗

  2. 官方提供的文档看上去没有入口函数

    只有一个 module.exports ,却没有说明在哪里使用这个模块,也没有提供运行命令

  3. 自定义操作复杂繁琐

    如上面的自定义用户系统,它说可以用“自定义数据库服务”来实现这个功能,于是去查看自定义数据库文档

    这个文档刚开始比较难读懂,但读懂之后明白了它的意思:

    我们的项目使用的是 Prisma ,这让难度更加上升(暂且不谈函数的执行方法)。我需要有一个数据库,用来执行 prisma migrate ,还要和我们的主项目同步 Prisma Schema 。这才会生成对应的 PrismaClient 。然后我就可以写出类似如下的代码:

    红框部分是官方给出的参考类型

  4. 函数执行方法复杂:

    因为我需要把它传入的参数做一个转换,使其作为 Prisma 的查询条件。这是官方给的查询方法实例:

    1. 普通查询:

      const model = new CustomModel('Comment');
      await model.select({
        url: '/',
        user_id: ['!=', 0],
        createdAt: ['>', '2023-04-16 00:00:00'],
      });
      // SELECT * FROM Comment WHERE url = '/' AND user_id != 0 AND createdAt > "2023-04-16 00:00:00";
      
      
    2. IN / NOT IN 查询

      const model = new CustomModel('Users');
      await model.select({ objectId: ['IN', [1, 2, 3, 4]] });
      // SELECT * FROM Users WHERE objectId IN (1,2,3,4);
      
      
      const model = new CustomModel('Comment');
      await model.select({ status: ['NOT IN', ['waiting', 'spam']] });
      // SELECT * FROM Comment WHERE status NOT IN ('waiting', 'spam');
      
      
    3. LIKE 查询

      const model = new CustomModel('Comment');
      await model.select({ content: ['LIKE', '%content%'] });
      // SELECT * FROM Comment WHERE content LIKE "%content%";
      
      
    4. 多条件查询

      const model = new CustomModel('Comment');
      await model.select({
        url: '/',
        user_id: ['!=', 0],
        createdAt: ['>', '2023-04-16 00:00:00'],
        _logic: 'OR',
      });
      // SELECT * FROM Comment WHERE url = '/' OR user_id != 0 OR createdAt > "2023-04-16 00:00:00";
      
      
    5. 复合查询

      const model = new CustomModel('Comment');
      await model.select({
        url: '/',
        _complex: {
          user_id: 0,
          status: ['NOT IN', ['waiting', 'spam']]
          _logic: 'OR'
        }
      });
      // SELECT * FROM Comment WHERE url = '/' AND ( user_id = 0 OR status NOT IN ('waiting', 'spam'));

    如果你比较熟悉 TypeScript,这里有条件查询的类型定义。

搏斗过程

对 Vercel 的困惑

我发现它对 Vercel 有着很好的支持,甚至这样就可以在 Vercel 上直接运行

这看上去也没有入口函数,可是为什么 Vercel 却可以直接运行呢。

尤其是 GPT 给出的回答,让我更加好奇:

Vercel 是一个云平台,专门为前端项目和无服务器(Serverless)函数提供部署和托管服务。
当您在本地使用 Vercel CLI 运行 vercel dev 命令时,它模拟了 Vercel 云环境的行为,
让您能够在本地开发和测试无服务器函数。

目前还没有这个精力去研究 Vercel ,留下这个困惑,之后再去和 Vercel 搏斗(

如何运行

所以话说回来,我应该如何在本地运行的同时,又能引入我的自定义模块呢

又经过一段时间的折腾,我尝试使用 express 来进行模拟。

#初次尝试(失败

第一次是按照官方的方法,使用 module.exports :

然后在 express 挂在 Application 路由:

结果以失败告终。

成功尝试

然后我换了一种方法,把 module.exports 改为一个变量接收,然后再把该变量放入 express 路由中,结果成功了!

代码如下:

import dotenv from "dotenv";
dotenv.config();

import UserModel from "./userModel";
import express from "express";

const Application = require("@waline/vercel");
const bcrypt = require("bcrypt");

const MyApplication = Application({
  model(tableName: string) {
    if (tableName !== "Users") {
      return;
    }

    return new UserModel();
  },
  encryptPassword() {
    return {
      async hashPassword(password: any) {
        const saltRounds = 10;
        const hashedPassword = await bcrypt.hash(password, saltRounds);
        return hashedPassword;
      },
      async checkPassword(password: any, storeHash: any) {
        const isPasswordValid = await bcrypt.compare(password, storeHash);
        return isPasswordValid;
      },
    };
  },
});

const app = express();
const PORT = process.env.PORT || 3000;

app.use("/", MyApplication);

app.listen(PORT, () => {
  console.log(`Server is running on http://localhost:${PORT}`);
});

运行成功截图

运行成功截图

但事情似乎还没完….

成功运行不代表能用

网站虽然跑起来了,但注册等基础功能并不可用,还需要继续折腾。

初步判断是由于数据库映射有问题,于是改写代码后再次尝试。发现还是运行不了。

于是在每个函数中增加 console.log ,发现控制台并没有输出,而且请求根本发送不出去,甚至连我没有改写的 comment 请求也无法发出:

而且意料之中的,我改写的 user model 也无法正常使用 :

再次怀疑运行方法

现在把怀疑对象转为我折腾出来的 express 启动方法。看起来这个方法并不能正常启动所需服务。

查看源码之后发现入口文件长这样:

应该是一个突破口。于是把源码拿下来,并在源码上进行修改之后,成功运行,但数据库的映射并没有实现。

再次检查代码发现有个地方出错,更改之后我的自定义部分有效果了,但是是错误效果。

由于之前是根据官网改的 index.js 文件,现在发现 vanilla.js 中也有类似代码,然后我擅自在里面加上一模一样的更改,结果成功运行,但数据库映射没有起作用。看来只能在成功运行和自定义模块起作用这两个中二选一了。。

结束

这让我深刻了解一件事情:本来开源就是个公益事,写不写文档都是随缘的


Life is a Rainmeter