Personal Bookmark Manager

Created: 2022-11-14

Status: In Progress

Idea

我原先一直使用的是start.me 的书签管理,但网络访问一直不是很顺畅,加载的很慢,可能我的书签太多了吧,再加上最近一直弹窗广告。。。

所以就有了迁移的念头了,这几周试用过国内外不同的在线书签管理应用,但都没有一个能满足需求的

因此就萌生了这么个想法。

Build

下面介绍下主要的几个开发重点,如需查看具体的开发过程(流水账),请查看这里。

首先,在Codepen上建个pen,方便实施查看,也不用本地搭建环境,在公司或者在家里都可以继续编程。

然后先用模板代码:

<!DOCTYPE html> 
<html lang="en"> 
  <head> 
    <meta charset="UTF-8"> 
    <meta name="viewport" content="width=device-width, initial-scale=1"> 
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/css/bulma.min.css"> 
    <title>My Bookmarks</title> 
  </head> 
  <body> 
    <header> 
      <nav> </nav> 
    </header> 
  <main> </main> 
  <footer> </footer> 
</body> 
</html>

是的,依旧使用Bulma的css框架, 真的很好用。

整体的UI设计参考Linkding的,简洁直观,个人很喜欢它的风格

我的设计呢,很简单,单栏模式,所有的操作均在当前页面上完成,减少跳转,比如点击书签编辑按钮,直接浮现对话框来修改。

经过几天的调试,整体效果如下:

发布第一版代码 ,并部署到Cloudflare的Pages上

接下来就是编写功能代码了,原先想直接使用Flask来搭建,毕竟用python真的很爽,但后来想了下,这个项目主要的目的是深入熟悉JavaScript和DOM,所以决定还是用纯js来实现。

考虑到数据存储,用SQLite最方便,但不知道cloudflare的Pages服务要怎么使用SQLite数据库。

搜索了下官方文档,发现还真可以,使用D1 databases即可实现,因为这个服务还是Alpha阶段,所以是免费的,看来还得做好备份。。

首先,创建个数据库

然后还需要创建个worker来供pages去操作这个db

创建完以后,进入Pages,选择对应的网站名称,进入Settings下的Funstions设置

在Functions的下方,绑定刚刚创建的D1数据库

注意,Prodction和Preview都需要绑定,如下图

接着绑定worker,同理,production和preview都要绑定,这里我使用同一个service和db,你也可以单独创建专用与preview的db和worker

由于要在本地操作db,所以本地需要安装wrangler: npm install -g wrangler

然后登录cloudfalre npx wrangler login

登录成功后,使用默认配置来初始化: npx wrangler init worker-bookmarks

然后,修改worker-bookmarks/wrangler.toml配置文件:

本地创建个测试db: npx wrangler d1 create d1_bookmarks

接着新建一个sql文件来新建table,关于字段类型,可查看该文档

/*createTable.sql*/
DROP TABLE IF EXISTS main;
CREATE TABLE main (id INTEGER,title TEXT,url TEXT,tag TEXT,page INTEGER,description TEXT,note TEXT,PRIMARY KEY (`id`));
INSERT INTO main(id,title,url,tag,page,description,note) VALUES (1,'Google','https://www.google.com','#search',0,'This is a description','This is a note'),(2,'Google','https://www.google.com','#search #google',1,'This is a description','This is a note');

这里我创建了以下几个列:

id text

title text

url text

tag text

page integer

description text

note text

对本地的db进行操作:npx wrangler d1 execute d1_bookmarks --local --file=./createTable.sql

测试下:npx wrangler d1 execute d1_bookmarks --local --command='SELECT \* FROM main'

没问题后,就可以往云上的db进行操作

npx wrangler d1 execute d1_bookmarks --file=./createTable.sql

测试下:npx wrangler d1 execute d1_bookmarks --command='SELECT \* FROM main'

然后发布这个woker的设置:npx wrangler publish

由于我们在网页端已经创建了同名的worker,所以它会提示是否覆盖,选择y即可

接下来就是编写api,涉及到fetch函数,完全不懂。。。看下MDN资料 ,同时参考官方的demo,照猫画虎下就可以了:

/*worker-bookmarks/index.ts*/
export interface Env {
	DB:D1Database;
}

export default {
	async fetch(request: Request, env: Env) {
	  const url = new URL(request.url);
	  const q = url.searchParams.get('q');
	  const getByPage = env.DB.prepare("SELECT * FROM main WHERE page = ?");
	  const getById = env.DB.prepare("SELECT * FROM main WHERE id = ?");
	  const getByTag = env.DB.prepare("SELECT * FROM main WHERE tag LIKE ?");
	  const getBySearch = env.DB.prepare("SELECT * FROM main WHERE title LIKE ?1 OR tag LIKE ?1 OR description LIKE ?1 OR note LIKE ?1");
	  var results;
	  if(!q){return new Response("Call /api/id/q?=1");}

	  switch(url.pathname){
		case "/api/id":
			results = await (await getById.bind(q).all()).results;
			break;
		case "/api/page":
			results = await (await getByPage.bind(q).all()).results;
			break;
		case "/api/tag":
			results = await (await getByTag.bind(`%${q}%`).all()).results;
			break;
		case "/api/search":
			results = await (await getBySearch.bind(`%${q}%`).all()).results;
			break;
		default:
	  }

	  return results ? Response.json(results) : new Response("Call /api/id/q?=1");
	},
  };

打开本地server服务:npx wrangler dev --local --persist,测试正常

在找资料的时候,发现SQLite是有rowid(oid)的,也就是说,其实不用专门添加一个id的列来自动递增,而且添加的id列貌似检索起来没有自带的rowid快,所以把id列删除,重新建表

/*createTable.sql*/
DROP TABLE IF EXISTS main;
CREATE TABLE main (title TEXT,url TEXT,tag TEXT,page INTEGER,description TEXT,note TEXT);
INSERT INTO main(title,url,tag,page,description,note) VALUES ('Google','https://www.google.com','#search #google',0,'first description','first note'),('Baidu','https://www.baidu.com','#search #baidu',1,'second description','second note');

通过几天的摸索,基本完成了初步的API编写,具体的index.ts代码可查看这里,返回代码和测试代码还没有编写。。但影响不大,应该能用,以后有空了再写。。。

接下来,就是给网站添加js代码了,

首先需要自动获取url的meta信息,比如标题和描述,同样,需要使用cloudflare的worker,新建一个worker,这次直接在CLI中创建:npx wrangler init getUrlMeta

查看官方文档,可以使用API: request

2022-12-06

几天下来没有明显进展,获取meta信息的功能一直实现不出来,主要是底层的原理不是很懂,先放一边吧。。

先做导入书签的功能,尽快做出能用的最小版本,先上线用了再说。

Conclusion

References: