How to hide API Key on Heroku & Github for React Apps

Image from Unsplash

阅读本文前,你需要大致了解:

✅ Git & Github

✅ JavaScript & React

✅ What is API, API Key & Environment Variables


最近做了某家公司的Online Assessment,内容很有趣,通过和第三方API交互设计并实现一个Web App。同时要求deploy在hosting平台上,比如Heroku, Github Pages, Google Cloud等,让用户可以不需要下载代码,直接在网页端运行。

欢迎大家来玩一下我做好的Web App – WeTalk.ai: https://fun-ai-response.herokuapp.com/ 因为本人的OpenAI的API token过期了,加上heroku开始收钱,本人因为抠门这两样东西都没有续费,所以这个web app已经不能用了。哈哈,但是文章还是有用的。

Web App逻辑并不复杂,第三方API的文档也写得很清楚,所以这部分我自信做得还不错。但问题在于找平台deploy我的Web App时出现了问题:如何在隐藏API key的前提下deploy Web App.

与第三方API交互时,对方会给每个提出请求的用户一个单独的API key,在我们写的代码里,需要用到这个API key才能得到对方的数据。而API key由于是每个用户独一无二的密钥,一旦泄露并被人恶意是用,会给平台和用户带来很多麻烦,甚至有可能给平台造成数据安全问题。

因此,在任何时候都不应该把API key直接写在代码里并上传到任何公开平台,包括Github公开的Repository. 有些第三方平台(比如我用的这个)会主动检测网络上是否有暴露的API key并自动替换,导致我的Web App因为换了密钥无法与第三方再沟通。

这一步我花了很多时间,走了一些弯路,最后在搜索多篇教程+猫咪的一起debug下顺利完成了!还要感谢Discord前端群的群友帮助和象友帮助。

在这篇里我会记录一下这次项目中遇到的有用的教程和踩过的坑,希望给后来人一些参考。

我接下来详细介绍的是Create React App + GitHub + Heroku的workflow. 相信其他的deploy service也是相同的原理。


1. Adding Custom Environment Variables in Create React App

设置方法有很多种,取决于使用的前端framework. 一般来说常常会用到npm的dotenv package. 原理是将API key写在 .env 文件中,并不上传到Github,仅供自己本地使用。

因为我这次用的是React bootstrapped with Create React App,它自带了dotenv library,因此不需要再额外安装。具体可见官方文档关于配置Custom Environment Variables的部分

有几点摘要如下:

  • Custom environment variables should begin with REACT_APP_
  • Any other variables except NODE_ENV will be ignored to avoid accidentally exposing a private key on the machine that could have the same name.
  • Changing any environment variables will require you to restart the development server if it is running.
  • access it by process.env.REACT_APP in your React app.

Stack Overflow的一个例子

  • .env文件中 – REACT_APP_BASE_URL=http://localhost:3000
  • service.js中 – const BASE_URL = process.env.REACT_APP_BASE_URL;

2. Hide API Key from Github

配置完成后,需要在repository的root directory的 .gitignore 文件中加入 .env

样例如下:

# misc
.DS_Store
.env

如果之前不小心已经commit或者push过 .env file到自己的repository,那么需要清理一下Git cache. 让最新的 .gitignore 可以track到目前repository任何不应该被track的文件:

  • 如果想untrack single file,只需要 git rm --cached filename
  • 如果想要untrack所有 .gitignore 中的file,步骤如下:
  • First commit any outstanding code changes (否则git add之后其他进度的文件会消失)
  • run git rm -r --cached . (This removes any changed files from the index
  • staging area)
  • then run git add .
  • then commit: git commit -m ".gitignore is now working"

链接中的注意事项里也提到了,再从git上pull下来的时候这些 .gitignore 中的文件就不会存在了,所以请做好本地处理。

另外,可以在repository中加上 .env.example 告诉其他developer该如何配置app中用到的key value pair.


3. Deploy on Heroku using Git

接下来需要聊聊deploy的问题。这次我尝试了Github Pages和Heroku.

最终Github Pages没有成功,我怀疑是我使用的第三方API专门track了Github来的request并且禁止了。每次我一部署在Github Pages上,我的API key就会被认为泄露,并自动更换,造成了很多痛苦。

如果碰到了可以接受Github Pages的第三方API,可以直接照着Github Pages的deployment来设置,也可以遵循Create React App官方的Github Pages deployment部分进行配置。推荐照着后者的教程,因为可以在deploy之后继续开发,不需要反复deploy,操作非常丝滑。

Github Pages配置Enviroment Variables的部分请参照Discord群的22Chestnut的提供的教程,感谢帮助。


之后我转战了Heroku. 首先如果在别的平台deploy过并按照CRA官方文档修改过 package.json请记得要改回来!我在这部分卡了好久,恢复以后就好了。

接着根据CRA的官方文档来deploy with Heroku.

  • 首先安装Heroku CLI. 我是PC,其他方法我怎么都配置不成功,所以最后用了官方不推荐的npm,最后也成功了。
  • verify installation – heroku --version
  • 跟着步骤login – heroku login

之后就是deploy到Heroku上。如果想在创建app之前就连好Heroku的deployment,需要follow这个教程

我因为已经在安装Heroku之前创建了App,所以步骤略有不同,省略了前几步。

git init # 当时我copy到了一个新的repository里,还没有传到Github上,因此需要init.
heroku create -b <https://github.com/mars/create-react-app-buildpack.git>
git add .
git commit -m "react-create-app on Heroku"
git push heroku master
heroku open

因为Heroku官网的教程和该developer自己的教程出现了一些不同:git push heroku mastergit push heroku main 的区别,我没注意到,因此有了一个报错,最后使用了这个Stack Overflow的办法解决了。

git push heroku HEAD:master 或者 git push heroku HEAD:main 取决于想要用哪个branch deploy.

原理参见这个帖子。指明了用当前的repo来commit到我要deploy的branch.


4. Pass API Key to Heroku through Environment Variable

那么文件都deploy上Heroku了,但是如我之前所说, .gitignore 中的文件包括包含了我API key的 .env并不会被commit到任何repository里。之后怎么将我的API key告诉Heroku呢?

答案是我通过Heroku的方式偷偷告诉它。有两种方式:一种是通过Heroku CLI,一种通过Heroku Dashboard. 参见这篇官方教程

如果是用的React,也可以看这里的教程, 本质上都是一样的。

我用的是terminal command – heroku config:set REACT_APP_HELLO='I love sushi!'

注意读文档的时候不能掠过信息,比如这里: The app must be re-deployed for compiled changes to take effect, because during the build, these references will be replaced with their quoted string value.

因此不管用哪种方式配置了我的API Key,我都需要重新deploy一次。

git commit --allow-empty -m "Set REACT_APP_HELLO config var"
git push heroku main

对于git repository来说,我们没有增删改减任何文件,所以直接 git add .git commit -m "re-deploy" 的话并不会触发heroku的re-deploy.

所以记得commit的时候一定加上 --allow-empty 才会有效果!


5. Sync between Heroku and Github

为了保险起见,commit + push完heroku之后,commit到Github也请加上 --allow-empty 保证能顺利commit.


Reference

最后附上我这次的repository供参考 –

GitHub Repo

并欢迎大家来玩我的Web App – WeTalk.ai: https://fun-ai-response.herokuapp.com/

感谢猫咪的一路帮助和安抚。

Kindle Scraper的技术总结和反思

Image from Unsplash

Kindle Scraper的使用教程请见:Kindle Scraper使用指南

本文简要地记录一下代码的逻辑,以便之后回顾、理解并优化,也欢迎有兴趣的朋友讨论和修改。

原生笔记My Clippings.txt的问题:

  1. 强制按照时间顺序生成笔记。这样造成的问题有:
    • 标注不根据书本页码和章节排列,有可能多次前后颠倒;
    • 如果同时看很多本书,笔记会被不同的书拆散;
  2. 自动生成的笔记格式冗余信息过多,不符合大多数人的记录习惯;
    • 每条都需要手动复制粘贴到个人笔记本;
    • 每条都需要手动修改格式;
  3. 无法识别重复的笔记
    • 删除并重复划线会导致重复的笔记,需要手动检阅删除;

Kindle对于购买书籍的笔记整理功能是不错的,可以在自己的账户导出需要的格式。但是对于个人文档就不支持了。如果笔记数量不多,那这些问题还是可以忍受的。但是随着用Kindle的时间增长,笔记数量不可避免地增多之后,手动整理就非常浪费时间,异常痛苦。

鉴于Kindle的笔记格式虽然多种多样,并不十分统一(呵呵)……但是仍然有迹可循,所以我想尝试用Python的方式来整理数据并导出。

网上一搜,发现英文系统的有不少人写过,思路不一,但是都无法简单修改并应用到中文系统。我挑了一个思路清晰整洁的代码kindler作为我的baseline,基于他的代码结构进行修改。

修改内容:

  1. 加入Kindle中文系统的适配;
    • 改用可以encode UTF-8的语法来读写文件;
    • 根据中文格式修改细节(见后面ParseDetails内容)。
  2. 加入笔记的位置信息location,方便日后在Kindle中回顾前后文;
    • 加入informationFrom(title),return tuple(locations, highlights);
    • 将locations和highlights一并写入导出的.txt和.csv.
  3. 一本书内,根据locations排序所有的highlights:
    • 在informationFrom(title)中sort数据
  4. 优化导出的.txt文件名;
    • 加入titleScraper(title),生成文件前替换特殊字符。防止出现遇到特殊字符,文件不会生成也不会报错的情况;
  5. 加入导出.csv的功能;
    • 加入importAsCsv()
  6. 删除Json相关功能.

重中之重ParseDetails(details):

数据整理的简要逻辑如下:

拆分行存为list -> 按delimeter所在位置判定location和highlights在list中的标号 -> 进一步简化location信息 -> location + highlights一起导出。

所有问题都出现在加粗环节。在这里我们掉过很多坑。原因在于没有想到Kindle生成的笔记格式那么不统一:导致我们在测试程序的过程中被卡住,回过头去被动地修改了很多遍。

猫认为我们应该在一开始就整理出所有不同的数据格式,而不是一次次在QA中修改。我同意,因为事后的修改因为我有点不耐烦所以conditional语句写得很没有系统性。

以下是格式相关总结:

常见格式:

==========
Bliss More (Light Watkins)
- 您在位置 #857-857的标注 | 添加于 2021年2月3日星期三 下午5:48:40

Focused thinking is thinking exclusively about the task at hand
==========

但并不是所有格式都如上所示。

与Location行相关的错误:

带有页码+位置信息

==========
When to Jump (Lewis, Mike)
- 您在第 26 页(位置 #435-438)的标注 | 添加于 2019年7月30日星期二 下午1:53:35 

With each new conversation, my voice gained confidence. Another older coworker put it bluntly: “Do you believe in yourself?” I said I did. “Who is responsible for how this jump turns out?” I said I was. “Then you have no risk in trying. You’re betting on yourself here. And you believe in that bet. You have no risk.”
==========

只有页码信息,没有位置信息

==========
自救指南  
- 您在第 18-18 页的标注 | 添加于 2014年10月26日星期日 下午3:30:48 

不管神经衰弱是轻还是重,恐惧都是其发生的根源。冲突、悲伤、内 疚或羞耻可能引发神经衰弱,但恐惧很快就后后来居上
==========

笔记信息:

==========
Kindle Paperwhite 用户指南(第 2 版) (亚马逊)
- 您在位置 #404 的笔记 | 添加于 2014年4月8日星期二 上午11:03:58 

试用
==========

解决办法:更细致的筛选。split字符串之后的list标号需要修改。

与Highlights相关的错误:

没有Highlihgts的书签信息:

==========
反脆弱--从不确定性中获益
- 您在位置 #657 的书签 | 添加于 2014年4月9日星期三 上午8:03:15 


==========

没有Highlights的标注(可能是Kindle的Bug)

==========
Bliss More (Light Watkins)
- 您在位置 #891的标注 | 添加于 2021年2月3日星期三 下午5:48:06 


==========

解决办法:如果highlights为空,则不导出。

之后可以打磨的地方:

  1. 继续打磨parseDetails(details)功能:简化、理清conditional语句。之后能同时使用英文和中文系统。
  2. 在导出的.csv 文件中加一列来区分:标注、笔记、书签;
  3. 加上UX部分,把数据处理部分放到后台,做成一个简单的网页工具,可以适用于不会用Github和Python的人群。不过:
    • 类似的产品有一些,可能只是做自己练习使用;
    • 不适用于不喜欢上传隐私信息的用户。

谢谢阅读。

Kindle Scraper使用指南

程序简介

使用Python自动处理中文Kindle系统的书摘文件“My Clippings.txt”,为每本书生成独立的标注、笔记文档,显示标注、笔记及其对应的位置,支持导出txt, csv格式。可将txt, csv导入Notion, Google Sheets或Evernote等平台整理为更易浏览、编辑的清爽格式。

运行本程序需要安装Python3, 本程序下载地址请见本柴Github

充满爱的开发团队: Shiba Woof (Dev) + Mavis Meow (QA)

整理开发思路和改进意见,在该Script的技术总结里。本柴是代码新手,欢迎大家友好地讨论、debug和report issues.

使用步骤

步骤一:从Github Clone Repository

Clone repo

步骤二:把My Clippings.txt拷贝至当前repo

把Kindle生成的书摘文件拷过来

步骤三:打开terminal运行程序

  1. PC用户打开Windows PowerShell
  2. Mac用户打开terminal
获取txt格式书摘
  1. PC用户请运行 python .\kindle_scraper.py importAsTxt
  2. Mac用户请运行 python3 kindle_scraper.py importAsTxt
  3. 本程序会在当前目录中新建一个文件夹 /highlights ,你的清爽书摘和对应的Kindle位置信息会被储存在 /highlights 文件夹里。
  4. 把导出的txt文件拷贝至各类平台进行后续编辑,拷贝至Notion的效果如下,数字代表位置,文字代表标注或笔记:
copy到Notion后长这样
notion批量导入tips

在Notion右上角菜单栏选择import:

Notion右上角小菜单

选择txt文件格式:

在弹窗中选中本程序处理好的清爽书摘们:

嗒哒~整理好的书摘被批量导入Notion!

Notion批量导入效果

在Notion内对书摘进行各种操作(编辑和注释)都很方便,看起来也很清爽。

在Notion里整理书摘
获取csv格式书摘
  1. PC用户请运行 python .\kindle_scraper.py importAsCsv
  2. Mac用户请运行 python3 kindle_scraper.py importAsCsv
  3. 本程序会在当前目录中新建一个文件夹 /highlights ,你的清爽书摘和Kindle位置信息会被储存在 /highlights 文件夹里。
  4. 请阅读terminal说明或本文“步骤四”内容来用Microsoft Excel打开 UTF-8 format的书摘文件。
获取书摘目录
  1. PC用户请运行 python .\kindle_scraper.py showTitles
  2. Mac用户请运行 python3 kindle_scraper.py showTitles
  3. 书摘目录会显示在terminal上。

步骤四:转换csv文件并导入Notion

4.1 打开highlights文件夹可以看见一家人都都整整齐齐:

4.2 打开一份空白Microsoft Excel, 选中Data -> From Text ->选择要打开的清爽书摘文件

打开空白excel文件

选择要处理的书摘

4.3 在弹窗里选择Deliminated. 并在File Origin选择UTF-8. 然后点击下一步。

经过UTF-8编码就显示中文内容了
File origin选UTF-8

4.4 Delimiters选择Comma,这样是为了把位置(数字)和内容(文字)划分成两列,然后点下一步:

选择分隔符

出现的预览长这样,选择finish.

数据预览

4.5 最后的窗口会询问你把文档插入哪个表单,选择现有表单或新表单都可以,取决于个人需求和喜好。

选择插入位置

4.6 导出的CSV文件长这样,可以直接复制粘贴到Notion的任何文档中。

最后生成的excel文件预览
导入Notion的csv文件预览,选择wrap cells可显示多行

获取程序的帮助文件

  1. PC用户请运行 python .\kindle_scraper.py help
  2. Mac用户请运行 python3 kindle_scraper.py help
  3. terminal中会显示帮助文件。

参考

kindler by sanjamaniam – 本柴的程序是基于kindler程序修改而成。对原程序改动会总结在稍后的技术文章里。

特别鸣谢

谢谢猫咪送的Kindle作为我的生日礼物之一,让我看了很多书并萌生了写这个Script的想法;

并且谢谢猫咪提供的6000+行My Clippings.txt让我们完善这个产品;

最后谢谢猫咪的感情支持?。