隐藏从 Meteor Methods 调用的秘密服务器代码

Hide secret server code called from Meteor Methods

我正在努力思考如何在 Meteor Methods 中对客户端隐藏秘密服务器代码。 The docs seem to imply 以下是其完成方式的一般模式。 https://guide.meteor.com/structure.html#using-require

(请注意,根据文档,我使用 require 而不是导入,因为 import 不能在条件块中使用。)

首先,这是一个在名为 methods.js 的文件中定义并在客户端和服务器上导入的方法:

/* methods.js */

let ServerCode
if (Meteor.isServer) {
  ServerCode = require('/imports/server-code.js')
}

Meteor.Methods({
  'myMethodName': function() {
    // ... common code
    if (Meteor.isServer) {
      ServerCode.secretFunction()
    }
    // ... common code
  }
})

其次,这是 /imports/server-code.js 中的秘密服务器代码,我尽量不将其发送给客户端:

/* server-code.js */

class ServerCode {
  secretFunction() {
    console.log('TOP SECRET!!!!')
  }
}

const ServerCodeSingleton = new ServerCode()
module.exports = ServerCodeSingleton

但是 当我检查发送到客户端浏览器的源代码时,我仍然看到我的秘密服务器代码被发送到客户端:

即使在进行生产构建时,我仍然可以搜索并找到 'TOP SECRET!!' 字符串。我觉得我对 require 如何工作的理解太天真了,但是 Meteor 文档让它看起来很简单。那么隐藏从 Meteor Methods 调用的秘密代码的正确方法是什么?

编辑:无视答案,没有解决 OP 对 DRY 代码或乐观更新的需求。

默认情况下,Meteor 对文件实施 eager-loading 方案。你的 methods.js 就是一个例子。

如果文件位于名为 "client" 的文件夹下的任意目录中,则该文件仅提供给客户端。同样,"server" 下的任何文件仅提供给该文件夹。这就是您处理确保文件仅提供给服务器的方式。

如您所见,"Meteor.isServer" 构造仅限制环境中 运行 的内容,而不是那里提供的内容。

我通常像这样实现 Meteor 方法:

server/some/path/stuff.js:

// this is only on the server
Meteor.methods({
    'getStuff': function() {
        return "stuff";
    }
});

client/some/path/clientStuff.js:

// this is only on the client, calling the server method
Meteor.call('stuff', function(error, result) {
    if (error && error.reason) {
        alert(error.reason);
    }
    else {
        // use result
    }
});

关于 require,你为什么要用它?

我看到您在 /imports 中有代码,Meteor 将其视为特殊代码并且不会急切加载。这告诉我你明确地导入了这些东西。

MDG 对目录结构、ES15 模块以及如何加载代码有一些强烈的建议,他们在这里详细介绍:

https://guide.meteor.com/structure.html

您会注意到他们不使用 require,并且指定模块的方式与您的做法不同。

我想我终于想通了。

简短的版本是,忽略这里说的;我认为它不正确或至少具有误导性:

https://guide.meteor.com/structure.html#using-require

并按照此处的说明进行操作:

https://guide.meteor.com/security.html#secret-code

更长的解释是:在server-only文件中import密码并将其赋值给一个全局变量。 然后,在 公共文件 中,使用 isServer(或 !isSimulation)有条件地引用该全局变量。

所以我原来的例子可能是 re-written 这样的:

/*   /imports/methods.js   */

// Note: no conditional use of require this time

Meteor.Methods({
  'myMethodName': function() {
    // ... common code
    if (Meteor.isServer) {
      ServerCode.secret() // <-- Defined as a global outside of this file!
    }
    // ... common code
  }
})

因此 密码 文件可能如下所示:

/*   /imports/server-code.js   */

class _ServerCode {
  secret() {
    console.log("Shhhhhh, I'm secret()!")
  }
}
// Here's the global variable:
SecretCode = new _SecretCode()

然后在 server-only 文件中它可能如下所示:

/*   /server/server-main.js   */

import '/imports/secret-code' // <-- declare the global
import '/imports/methods' // <-- use the global in here

然后在 client-only 文件中它可能看起来像这样:

/*   /client/client-main.js   */ 

import '/imports/methods'

//...

Meteor.call('myMethodName')

现在客户端和服务器都可以在方法体中执行一些完全相同的代码(DRY),而一些秘密code 可以是 server-only 并且也不会发送给客户端。不得不求助于使用全局变量有点烦人,但也许这是最干净的选择,直到出现支持 built-in lazy-loading 模块的更漂亮的 import 版本。