标签归档:开发经验

使用 Proxy.py 自建 HTTP Proxy

使用 Proxy.py 自建 HTTP Proxy

如果你需要对一个集群进行开发,且开发过程中需要访问内网,一个比较常见的方式是建立虚拟专有网。但倘如你不愿建立虚拟专有网,那么一个比较简单的方式是建立一层 HTTP Proxy 来访问内网当中的数据,你只需要在某一个节点上设定 HTTP Proxy,并在你自己的电脑上配置 HTTP Proxy ,就可以完成使用对应的节点来访问具体的网页,解决 HTTP 访问的问题。

原理

HTTP Proxy Servr 的原理并不复杂,就是一个标准的正向代理。你只要能搭建这样的一个服务即可。

实际操作逻辑

以下文章以 Debian GNU/Linux 10 为例

1. 在你的节点上安装 Python3 & pip 3

Python3 是运行 Proxy.py 的运行环境,而 pip3 则是安装 Proxy.py 的工具。执行如下命令来安装。

apt install python3 python3-pip

2. 安装 Proxy.py

这里我们不使用 Virtual Env 来安装,主要是方便后续加入 Systemd 来进行控制。如果你习惯使用 Supervisord 来控制的话,就可以使用 Virtual Env 来安装 Proxy.py。执行如下命令

pip3 install proxy.py

3. 启动 Proxy 进行测试

在你的节点上执行如下命令来启动一个测试服务器。

proxy --hostname 0.0.0.0 

如果你需要监听某一个特定的 IP, 则将 0.0.0.0 修改为你具体的 IP。

配置后,你可以看到如下的输出,则说明已经成功启动了 Proxy 服务器。

启动完成后,你就可以在本地测试访问如下命令来进行测试。

curl -x [服务器IP]:8899  http://httpbin.org/get

当你看到如下输出时,确认返回值当中的 origin 是否是你的节点 IP,如果是你的节点 IP 则说明你的 HTTP Proxy 已经配置成功了。后续你就可以通过这个 HTTP Proxy 来进行访问了。

测试完成后,你可以执行 Ctrl + C 来关闭当前的 Proxy Server。

4. 配置 Basic Auth Authentication

上面配置好了 HTTP Proxy Server,但一个问题是这个 Server 没有安全验证,随时会被别人所利用。所以我们需要配置一些基本的安全配置,来解决这个问题。

在你的 Proxy.py 当中加入 –basic-auth 选项,则可以实现加入基本的鉴权。其中,用户密码用英文冒号隔开。比如我需要创建一个用户名是 admin ,密码也是 admin 的proxy ,就可以执行如下命令。

proxy --hostname 0.0.0.0 --basic-auth admin:admin

5. 配置开机自启动

现在 Auth 也有了,但是我们不能总是开着 Terminal 来运行我们的服务,所以一个好的办法是为其加入 Systemd ,这样我们就可以使用系统自带的能力来完成 Proxy 的自启动了。

执行 systemctl edit --force --full proxy.service 来创建一个新的 Systemd 服务,并在其中加入如下代码并保存。

[Unit]
Description=Proxy Service
Wants=network.target
After=network.target

[Service]
ExecStartPre=/bin/sleep 10
ExecStart=proxy --hostname 0.0.0.0 --basic-auth admin:admin
Restart=always

[Install]
WantedBy=multi-user.target

保存完成后,执行 systemctl enable proxy.service 配置该服务器的开机自启动 & systemctl start proxy.service ,即可实现 Proxy 服务的开机自启动,并在当前启动该服务。

总结

有了 HTTP Proxy ,你能以一个更加简单的方式来访问内网当中的服务器。你只需要控制 Proxy 节点的可访问权限,就能很好的处理内网数据的安全问题。

记录一个令人血压飙升的前端项目

记录一个令人血压飙升的前端项目

最近在看一个前端项目,里面出现了一些令人血压暴涨的骚操作。虽然可以用,但对我来说,确实是让我看到血压飙升。

1. 尽可能使用常规的指标

项目的诉求是实现页面的自适应化,在不同尺寸的屏幕上尽可能保持一致的显示。

为了实现这个诉求,我给他们的建议是使用 Bootstrap 之类的框架,在不同的屏幕上尽可能保持一致。使用 Bootstrap 之类的框架来完成。

最后研发团队选择采用了一个有用,但我看起来非常头疼的方法 —— 根据屏幕宽度计算 rem。通过这样的方式,实现了在不同的屏幕宽度下,都可以和设计稿的尺寸一比一。

但这样带来的问题是,rem 计算出的结果也是一个非常的大的值(当然也可以非常小),但依然不是一个常规的结果,需要你常常使用诸如 0.03 这样的数值来指定宽度和高度。

我觉得,这样的写法并不是说不行,只是这样的写法其实会降低整个项目的可维护性。

2. 文件放置符合规范

其次,这个项目中还出现了一个问题是 —— 文件到处乱丢。在一些项目中,大家往往会有一些明确的文件路径的规范,比如样式文件应该放在哪里。在这个项目中,就出现了样式乱丢的情况。

和 Component 放在一起
符合规范统一放在 scss 文件中

这样的混乱的写法则会导致在实际开发的时候,如果想要调整样式,可能会出现调试失效,修改起来较为混乱的问题。

3. 一个项目中混用多个不同的尺寸单位

另外一个文件采用 px 作为单位

刚刚有用 rem 的单位,另外就有一个文件采用的是 px 为单位,我非常担心在不同尺寸屏幕下出现的错位问题。

4. 放弃 Bootstrap 的样式类,转而改用手动撰写每一行样式

其实完全可以用 position-absolute d-flex 等方法来覆盖

总结

这个项目从我目前的视角来看,依然还有很大的改进空间。且留下了不少的坑。在我看来,长期来看,这些代码将会留下不可维护的问题。希望他们后续慢慢修改和调整吧。唉。

在 WordPress 中实现同一个 CSS 类在不同页面展示不同的效果

在 WordPress 中实现同一个 CSS 类在不同页面展示不同的效果

在使用 WordPress 开发时,某些时候你会遇到在同一个类在不同页面的时候,希望有不同表现的诉求。在这种情况下,一个比较简单 or 比较 Tricky 的技巧是通过 Body 层的类选择器来实现。

在 WordPress 进行渲染时,会自动在页面的 <body> 上加入 postid-[id] 的类,对比本文就是 postid-5888

比如本文的 class

类似的,如果是一个 Page,也会生成 page-id-[id] 的类。

页面生成的类是 page-id-[id],和 post 规则不完全相同

因此,如果你需要某个页面的元素样式和其他页面元素样式不同,但页面结构保持一致,一个比较好的方式是通过这些页面类选择器来实现。以你要自定义 btn 样式为例。则可以这样实现:

// 标准页面的样式
.btn{
   // your styles
}

// 在文章 ID 为 5888 的文章上的样式
.postid-5888 .btn{
  // your styles
}
// 在页面 ID 为 84 的文章上的样式
.page-id-84 .btn{
  // your styles
}

总结

借助上面的方式可以实现同一个类在不同的页面展示不同的样式。但我个人觉得,这样的实现方式可能对于 Debug 不太方便,尽可能少用。但如果你需要用的话,那一定要注意好组织代码,避免后续维护成本提升。

此外,除了通过修改 style.css 来实现,你可以考虑将 Page 单独的样式梳理在一个 page.css 中进行维护,从而降低维护难度和成本。
WordPress 当中也有一些插件来实现某个页面自定义的 CSS 能力,将这些样式放在插件中来实现也是一个不错的选择:Post/Page specific custom CSS

Reference

自定义 Bootstrap 5 的风格,实现自定义风格页面开发

自定义 Bootstrap 5 的风格,实现自定义风格页面开发

和更加自定义化的 TailwindCSS 相比,Bootstrap 显然更具备主题和模板页面开发的潜力和更易于进行模板页面的开发 —— 毕竟各种样式都已经封装好了,你可以直接使用诸如 btn 来定义一个 Button。

我有在想,提供一个基于 TailwindCSS 的 StarterKit 来快速基于 TailwindCSS 开发出一套自己的样式库,但仔细想想,其实意义不大。因为可选项太多了,想要自定义化其实没那么容易。

白宦成

而在开发 Bootstrap 的时候,大家往往会遇到的一个问题的是 —— Bootstrap 开发出来的页面千篇一律,一千个人有一千个哈姆雷特,但一千个 Bootstrap 页面却长的一模一样。而这背后的原因,主要是因为大部分人使用的是标准版的 Bootstrap —— 也就是官方所定义的样式。大家都使用一样的样式,自然就使得开发出来的页面显得雷同。

不过,其实 Bootstrap 的风格自定义并不困难。

前情提要

其实大家不去做自定义风格开发的原因倒是可以理解 —— 锅都要给 Node Sass,在 Bootstrap 5 以前,Bootstrap 使用 Node Sass 来进行样式的构建,但 Node Sass 是一个基于 C++ 编写的拓展,这使得 Node Sass 的安装十分麻烦,加上特有的网络环境,使得 Node Sass 的安装十次有九次都是无法成功安装的,大家自然也不愿意使用 Node Sass 构建的项目(包括我自己,都会刻意选择使用 Less 构建的项目)。

不过,从 Bootstrap 5 开始,Bootstrap 项目开始使用 Dart Sass 进行样式风格的开发。这使得 Bootstrap 的构建不会像过去那么痛苦,因此也可以更好的进行风格的自定义。对于如今还在使用 Bootstrap 进行项目构建的同学来说,无疑是个好消息。

自定义 Bootstrap 5 的风格

关于如何在你的项目中引入 Boostrap 的 Sass 来满足自定义的需求,可以参考官方所给出的 Customize Sass 的说明,官方提供了包括 WebpackParcel 等的接入说明,如果你连基本的配置都懒得做,官方还提供了一个空白项目,方便你 Fork 后再修改。

以我自己使用的 Next.js 为例,配置起来也比较简单:

1. 安装相关依赖

在 Next.js 项目根目录安装所需依赖

yarn add bootstrap
yarn add -d sass sass-loader

2. 创建 Scss 文件

styles 目录下创建一个新的 globals.scss 文件,并在其中加入如下代码

// custom variables
@import "../node_modules/bootstrap/scss/bootstrap";
// custom css code 

其中 custom variables 的部分是让你用来加入自定义的变量的,比如自定义 Primary 的颜色、自定义 Padding 等一系列操作,都需要在 Bootstrap 的引入前定义,这样才能确保你的引入变量会生效。

而后面的 custome css code 则是可以用来定义一些你自己编写的,没有被覆盖在 Bootstrap 内部的类。

3. 在项目中引入创建好的 scss 文件

在项目的 pages/_app.js 引入我们刚刚创建好的 scss 文件,从而确保应用在启动的时候可以自动构建 Bootstrap ,并引入至项目中。

import '../styles/globals.css'
import '../styles/globals.scss' // 这一行是新增的
function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />
}

export default MyApp

4. 在项目中使用 Bootstrap 类进行开发

当你完成上述的配置,就可以在自己的项目中引入相应的类来进行开发了。

一些小技巧

使用 Themestr.app 进行风格的快速设定

Themestr 是一个帮助你快速生成一套Bootstrap5自定义风格的工具网站。

在 Themestr 中,提供了 UI Builder、Themer 和 Customizer 三个不同的工具,其中:

UI Builder 提供了一个简单的 4 步选择器,让你定义了基本的颜色、字体、ICON 和 按钮风格,从而形成一套独特的 Bootstrap UI。

Themer 则提供了更加全面的样式的设定和预览功能,你可以在右侧的选择界面简单的配置样式,并在左侧自动刷新的页面中查看你的配置所实现出来的效果,方便你快速找到看到你的配置所能展现出的效果,简单且直观。

Customizer 则提供了全面的 Bootstrap 的变量,方便你快速找到你要修改的变量,并帮助你生成对应的 Sass 配置文件,方便你在此修改变量,并在本地进行开发。

对于不同的场景,Themestr 提供了不同的工具来帮助你快速开发,是一个非常不错的 Bootstrap 主题开发的辅助工具。

如何找到需要修改的变量

Bootstrap 在文档中针对每一个 Component 都提供了相应的 Sass Variables 的说明,你只需要在文档中找到你要修改的组件, 并在你自己的 global.scss 中修改对应的变量,就可以实现自定义。

如何找到特定类的源码

在进行 Bootstrap 开发的时候,如果有些类你找不到样式的时候,一个很好的方式是开启开发环境,并在开发环境中可以看到对应的 scss ,并在对应的 scss 当中,点击链接跳转到对应的 scss 定义,即可看到对应的样式的源码。

在跳转后,有些时候你发现似乎并不是直接定义的样式,比如下图,则说明这样的类是通过 map 来批量生成的,你就需要修改对应的 map 来完成自定义。

如何自定义主题颜色、字体大小、间距等批量生成的类

在 Bootstrap 中存在一些批量生成的类,比如 theme-colorfont-sizefont-weight 还有 paddingmargin 等等。

这些类是 Bootstrap 使用 @each 来生成的,而生成的依据,则是 Bootstrap 的各种 map 来完成的。因此,你想要进行定义,则需要修改对应的 Map。你可以使用 map-merge 来向 map 中添加新的选项

// Create your own map
$custom-colors: (
  "custom-color": #900
);

// Merge the maps
$theme-colors: map-merge($theme-colors, $custom-colors);

而如果你想要移除一个选项,则可以使用 map-remove 方法来完成

// Required
@import "../node_modules/bootstrap/scss/functions";
@import "../node_modules/bootstrap/scss/variables";
@import "../node_modules/bootstrap/scss/maps";
@import "../node_modules/bootstrap/scss/mixins";
@import "../node_modules/bootstrap/scss/root";

$theme-colors: map-remove($theme-colors, "info", "light", "dark");

// Optional
@import "../node_modules/bootstrap/scss/reboot";
@import "../node_modules/bootstrap/scss/type";
// etc
OneInStack 中 Redis 的使用注意事项

OneInStack 中 Redis 的使用注意事项

在使用 OneInStack 时,有一些事项需要注意,不然可能会导致你的 Redis 在使用过程中出现问题。

1. Redis 默认配置内存是 122 MB

OneInStack 中 Redis 的默认内存配置是比较小的,只有 122MB ,对于一些大型应用来说,是肯定不够的,因此,在实际使用过程中,还是最好将其修改为一个更大的值。

配置文件路径: /usr/local/redis/etc/redis.conf

需要修改的项目 maxmemory 122000000,将 122000000 修改为 256000000 即可将 Redis 使用的内存设置为 256 MB,从而扩大了整体可用的内存的大小,应对大型数据库也游刃有余。

2. Redis 的逐出机制为 noeviction

OneInStack 中 Redis 的默认逐出机制是 noeviction,即内存满后不逐出,写入缓存报错,读缓存不受影响。

对于将 Redis 作为数据库的场景而言,这么干是正确的。但对于做缓存的场景,则需要修改逐出机制,比如可以将逐出机制修改为 allkeys-lru,即在全部的key中淘汰最近最少使用的key。

在配置文件( /usr/local/redis/etc/redis.conf)中加入一行配置即可

maxmemory-policy allkeys-lru
那些影响你前端开发体验的问题(1)

那些影响你前端开发体验的问题(1)

最近在研究和体验一些 GitHub 上的前端项目,遇到了一些让人体验不佳的点, 这里梳理一些我遇到的,一方面是留存,记录那些让我体验不好的事情。另一方面警示自己在开发前端项目的时候应该注意一下开发体验,确保不会让别人在开发的时候也遇到这些问题。

1. 项目应该有文档

这是不少 GitHub 上面项目的普遍问题。现有的文档要么是以 Framework 生成的 Readme 为主,要么直接没有文档。

但没有文档对于后来者的开发其实非常不友好,比如几个常见问题:

  1. 如何启动一个 Server & 如何设置后台 API 的 Prefix?
  2. 项目的组织结构是什么样的?
  3. 涉及到的第三方服务应该如何配置?

上面这些文档是帮助后来者快速使用你的项目所必需的(换句话说,如果你要做开源项目,想要打造开源社区,这也是必要的)。

2. 项目控制算法复杂度

我在开一些前端项目的时候,明显会感到浏览器的响应变慢了,我以为是我的 Mac Mini 2012 性能不行。但我打开自己的博客,依然如丝般顺滑,我就明白了。问题不在于我的电脑性能跟不上,而是这个前端页面加入了大量的有用或无用的代码,使得 CPU 需要大量的计算。

虽然进入 SPA 的时代以后,我们开始将算力的使用从服务端往客户端转移,但也建议各位工程师控制自己项目的复杂度,不要一开网页,风扇就呼呼的转。浏览体验不好,还凸显自己的技术不行。

3. 使用 debuuger 调试,而不只是 console.log

在编译型语言当中,使用 Debugger 来进行调试是一项基本技能,因为需要在运行时去看不同的变量的值,以此来完成调试。

爆炸的 Console

但在前端领域,因为 console.log 的过于好用,大家开始习惯于使用 console.log 来打印变量,但对于大型项目来说,大量的 console.log 也会让你的项目输出许多无意义的内容,让整个项目的调试和开发受到阻碍。

总结

最近看项目,总结了三条自己觉得影响体验的点,后续随着看的项目的深入,会逐渐总结出更多影响体验的点,引以为鉴。

WordPress Contact Form 7 的正确用法

WordPress Contact Form 7 的正确用法

Contact Form 7在实际使用过程中,有些人为了简化表单的设计和使用,会选择直接在多个地方使用同一个表单,并通过页面选择器来实现不同的样式(常见于基于 Elementor 构建的页面)。

但这样会造成长期的可维护性问题:

  1. 每个页面单独写样式选择器来控制样式,且在样式命名方面没有习惯的话, 会导致页面的样式混乱,修改成本较高。
  2. 不同的页面的样式采用了相同的类名,在进行样式修改时,交接性极差。

如果想要解决这个问题,有两个方式:

  1. 规范页面样式类规范:不同风格和样式的按钮采用不同的类名,如(.blueBtn.yellowBtn);
  2. 多处使用的表单,采用复制的方式,而不是使用同一个表单:Contact Form 7 提供了 Duplicate 功能,可以非常方便的复制一个表单,并根据表单的位置来命名。降低配置表单的成本。
Duplicate 一个表单
从一封钓鱼邮件聊起:针对普通人的钓鱼邮件设计

从一封钓鱼邮件聊起:针对普通人的钓鱼邮件设计

收到了一封钓鱼邮件,刚好最近没有什么内容要写,就聊聊这一封钓鱼邮件。

我收到的这一封钓鱼邮件是这样的

接下来看看里面的钓鱼邮件设计的三个巧妙之处:

1. 针对独立域名的钓鱼邮件

我的对外的邮箱目前使用的是 bestony@linux.com 的邮件,而由于 Linux.com 邮箱设计,实际上并不会有一个邮箱给你使用,而是你可以选择一个邮箱地址,系统会将发送到这个地址的邮件自动转发给你,我将邮件转发到了我自己的 Google 邮箱当中。

所以实际上我收到的邮件有两种:以 gmail 地址收到的邮件和以 linux.com 地址收到的邮件。

可以看到,上面的这个邮件当中我的收信地址是 linux.com 的地址,而不是我的 gmail 地址。

这正是这封邮件设计的巧妙之处:为特定人群发送特定内容的钓鱼邮件。试问自己:如果你的 QQ 邮箱收到了上述的邮件,你会把他当成是企业给你发送的安全邮件么?显然不会,因为你知道, QQ 不会给你发送这样的邮件。

但如果你的邮箱刚好是一个自定义域名,且刚好你所在的企业的 IT 并没有拦截到这封邮件,那么这封邮件对于那些安全意识不高的人来说,马上就会中招。

2. 使用了一个内网的地址来降低警惕度

对于绝大多数人来说,可能对于内网地址和外网地址没感知。可能直接就点击进去了。但对于一些对于计算机网络略有耳闻的人来说,可能会熟记的一个地址是 192.168.0.1,这个地址被不少的路由器作为默认的地址和网关地址来使用,从而成为不少人的心中的安全地址。

邮箱中的 192.168.22.23 这个地址就会让一些人放下警惕,然后点击进去查看内容 —— 然后成功的掉进陷阱中。实际上邮件只是用内网地址来作为一个表面展示的文字,真正的链接地址是 http://szfdxled.com/function/uploadfile/20220412/20220412000529_78464.html#bestony@linux.com

这提醒了我们,如果邮件当中有链接,最好复制出来,而不是直接点进去(说不定别人替换了呢?)

3. 自动识别的邮箱域名

我点击链接进去以后注意到,他在顶部加入了对应的邮箱域展示和 Icon 的展示。设计的挺巧妙。

如果点击进去展示的是无关的域信息,可能你也不会点击进去查看。但如果展示的是你自己的邮件域,会进一步放松警惕(特别是你以为你点击进去的其实是 192.168.xxx.xxx 时)。

对于意识不太强,或者平时不常使用网页版的人来说,可能真的就直接输入账号密码登录了。我还试了试,如果把 URL 后缀的邮箱域修改了,还会自动替换邮箱域和对应的 icon(应该是抓的 favicon,但不知道为啥抓到的是这个。

不过,也有一些设计的比较蠢的地方

1. 用了 Reply To 的字段暴露了自己的信息

在看这封邮件的时候,我注意到他设定了 Reply To 字段。Reply To 当中暴露了自己的 QQ 邮箱。

然后我搜了一下,发现能搜到这个人,看起来似乎还像是正常使用的 QQ 号。。。如果是一个经验丰富的 Cracker ,可能会选择使用一个更加安全的沟通方式。

总结

这封钓鱼邮件中给我不少的启发,里面的一些设计也很有意思。但对于有充足安全意识的人来说,这些问题确实都可以规避掉,从而不受诈骗的影响。此外,Cracker 的产品设计值得我们学习,一些好的设计,确实可以帮助用户更省事。

为什么硬件产品总有遗憾?

为什么硬件产品总有遗憾?

在做软件产品时,我们会无限的追求极致,希望将产品的体验、用法等各个方面全部做到最好,因为软件的交付成本低于硬件的交付成本,我们只需要一个命令,就可以将旧版的软件升级到新版。因此,我们可以通过不断的软件迭代,来将一个不完美的软件,迭代到完美的状态下。 而硬件则不同,硬件涉及到两个问题:

  1. 硬件存在供应链和备货的问题:这就会要求你必须提前采购物料,并进行研发。物料是实体的,必然会存在出问题的可能,因而会让我们的硬件设备出现各种各样奇奇怪怪的问题,天然就不太可能完美。
  2. 硬件存在升级成本的问题:硬件和软件的一个很不同的是,硬件往往是很难做到很好的升级的,想要升级,就不得不重新购买,而购买是需要花费金钱的。不是每个人都能购买新的设备的。而从厂商的角度来看,假设他做了一款完美的产品,他就无法再继续售卖同类型的产品,自然也没有动力去推出一款「完美」的产品

上述两个原因,导致我们永远不可能买到完美的硬件产品。

做 Leader,别做 Boss

做 Leader,别做 Boss

在做管理时,不要想当然的以为 Ctx 已经传递了。还是会有很多不明确的信息。

如果你希望更多的人帮你去做事,需要传递足够做的 Ctx 出去。可能这些你过去觉得不重要的 Ctx,但对于那些不懂的同学来说,这就是必要的 Ctx。

如果只有一个任务,那你是 Boss ,最终会众叛亲离。但如果你给到的是一个完整的资料,那你会成为 Leader,领导别人去做成一件事, Empower 别人去做一件事。

受教。

工单效率这么低,为什么还要选择工单服务?

工单效率这么低,为什么还要选择工单服务?

为什么觉得工单的效率低?

和语音聊天、 IM 沟通,工单系统作为一个「问答」形态的产品,必然带来是消息回复的不那么及时,你也很难像 IM 一样,一波语音通话,直接打过去,让对方的同学接听。 但,这就意味着效率低么?

也不尽然,我们觉得语音通话和IM 沟通能够提高效率是因为 能够在很短的时间内尽可能多的传递信息给对方。但这并非语音通话和 IM 沟通才能提供的,实际上,我们用好异步交流的方式,同样,甚至可以更好的传递信息。正是因为你将工单系统当作 IM 系统来使用,才造成效率低。

什么是好的工单系统的用法?

工单系统的异步沟通回复实效性差,但相应的,也倒逼我们必须在尽可能少的消息当中补充足够多的上下文,帮助工单服务人员来更好的协助我们巡查问题。比如,附带上出问题的条件和代码、提供信息帮助对方定位、提供复现的方式(是不是觉得很眼熟?我们在开源社区也被要求在提交 issue 的时候尽可能多的提供信息)。 当我们提供了尽可能多的信息的时候,我们才能更好的让工单系统起到作用,才能更好的传递信息,解决我们的问题。

工单引发的全局最优和局部最优的思考

从工单系统出发,我又想到了一个话题 —— 「全局最优和局部最优」。我们常说,要追求全局最优解,而不是局部最优解。就比如腾讯停止某个不那么擅长业务,显然,从局部来看,对于负责对应业务的同学来说,这不是一个最优解。但对于公司层面来看,释放人力去做更有价值的事情才是最优解。

为什么会选择工单?

和语音通话/IM 沟通相比,工单系统的优势在于何处?语音通信和 IM 沟通虽然可以很方便的将信息传递,但对于信息的沉淀和再次利用,并没有什么帮助。实际上用户并不会主动在历史当中搜索。

你会发现,用户并不会主动搜索历史问题,他们更喜欢直接提出问题,然后等待解决。 既然如此,那工单系统所承载的服务,自然也就能够提供不同的和更加强大的能力,帮助 oncall 同学来解决服务的压力。

工单并不完美

当然,现在的工单系统并不能做到想要的最优,比如在我看来,这里还有很多东西是我们没有做到的,比如自动记录用户进入 oncall 页面的 path(这样 oncall 同学可以直接知道用户是在哪个模块出现的问题)、自动记录提示用户选择 service id(这样就不用每次都提醒用户输入 service id)、自动根据用户输入的信息提供可能的回复。

可以看到,这里面其实是有很多的 gap 没有处理好的。也正是这些没有处理好的事情,让现在的 oncall 系统看起来不那么的美好。 但你我都知道,事情总是在演进的,现在不好不意味着他不能让事情变得更好。让工程师直接提供 oncall 固然可以让服务变得更好,但对于业务的可持续性、成本的优化和不断的产品迭代来看,并不能算是一个最优解。

而客服系统其实是一个标品,我们会看到 ZenDesk、Tawk.to、Intercom等等一系列的客服系统,都在提供服务,从某种角度上来看,这验证了客服系统/工单系统的方向的正确性。而一个好的客服系统,应该是让用户很快得到帮助(比如自动帮助用户搜索问题),让工程师可以更加专注(比如把问题的解决更好的总结成经验)。

使用 Stylish 巧妙破解基于 CSS 的复制限制

使用 Stylish 巧妙破解基于 CSS 的复制限制

本文内容仅用于学习和技术研究。请不要用此作恶。

白宦成

在一些网站上,出于版权或增长的诉求,一些网站会在站点内部加入防止复制内容的限制。作为一个内容创作者,我是支持(因为版权原因而不得不选择)这样的行为的。但另一方面,我自己也需要进行一定的笔记,对于长段的内容,显然,直接复制原作者的内容更适合我。因此,我也不得不研究一下,如何破解这种样式的限制。

为什么 CSS 能禁止复制?

CSS 当中提供了一个属性 user-select 可以用于设定文字是否可以被选中,对于非技术人员来说,无法被选中,自然无法使用 Ctrl + C 来复制内容。在这种情况下,对于非技术人员,就可以实现很好的防复制了。

不过,CSS 防复制不算多见,我见的最多的还是以 Javascript 的方式来禁用复制和 Devtools 的。

这个属性的值有以下五种,其中 none 是用于防止复制的,设定为 None 后,即使你的内容是文本内容,也无法被选中,就无法直接使用 Ctrl + C 来完成复制了。而 text 则是可以选择文本,其他的几个属性,你可以在 MDN 的语法说明中找到。

user-select: none;
user-select: auto;
user-select: text;
user-select: contain;
user-select: all;

使用 Stylish 来破解 CSS 的限制

类似油猴脚本可以在目标网页上加载 Javascript 文件,Stylish 可以在目标网页上加载 CSS ,并按照 CSS 的计算权重,让我们的代码可以得到执行。基于这个能力,我们可以覆盖原本网页上样式,来实现将 user-select 的值修改为我们需要的值。

1. 安装 Stylish

前往 Google Chrome Extension Store 安装 Stylish

2. 找到不能复制的文字对应的样式类

在浏览器中打开开发者工具,定位到你需要复制的文本元素,在右侧的调试工具中切换到「计算样式」,就可以在其中找到 user-select属性,并可以看到对应的样式类选择器,复制选择器。

3. 在 Stylish 中新建一个样式文件,并在其中添加对应样式类的代码

因为我们只需要修改复制,所以只需要将对应的属性的值做一个简单的修改即可。

4.设置生效网站

为了减少这段代码影响到的页面,你可以在下方的应用对象这里设定具体生效的网站,从而降低对于其他站点的影响。

总结

本次针对 CSS 禁止复制的研究帮助我节省了大量的研究的时间,有效的提升了我进行研究的效率,真的不错!