Inbox

IndexedDB Demo

IndexedDB Demo #

interface IData {
  componentName: string;
  propName: string;
  voId: number;
  renterKey: string;
  componentName_voId: string;
}

class Database {
  private static _instance: Database;

  private connection;

  constructor() {
    const openRequest = indexedDB.open('development-database', 1);

    openRequest.onupgradeneeded = (e: any) => {
      const db = e.target.result;
      console.log('Database upgradeneeded');

      if (!db.objectStoreNames.contains('voStore')) {
        const store = db.createObjectStore('voStore', { keyPath: 'componentName_voId' });
        store.createIndex('componentName_voId', 'componentName_voId', { unique: true });
        store.createIndex('componentName', 'componentName', { unique: false });
        store.createIndex('voId', 'voId', { unique: false });
      }
    };

    openRequest.onsuccess = (e: any) => {
      const db = e.target.result;
      this.connection = db;
    };

    openRequest.onerror = (e: any) => {
      console.log('Database init error', e.target.error.name);
    };
  }

  static getDB() {
    if (!Database._instance) {
      Database._instance = new Database();
    }
    return Database._instance;
  }

  // 更新所有跟 componentName 相关的数据
  public updateByComponentName(list: IData[], componentName: string): Promise<{ code: number; msg: string; }> {
    return new Promise((resolve) => {
      const tx = this.connection.transaction('voStore', 'readwrite');
      const store = tx.objectStore('voStore');

      this._searchByComponentName(componentName, store).then((data) => {
        const listByName: IData[] = data;

        list.forEach((item) => {
          if (listByName.some((l) => item.componentName_voId === l.componentName_voId)) {
            // update
            const putRequest = store.put(item);
            putRequest.onsuccess = () => {
              console.log('update');
            };
          } else {
            // new add
            const addRequest = store.add(item);

            addRequest.onsuccess = () => {
              console.log('add');
            };
          }
        });

        // remove not found
        const removeList = listByName.filter((l) => list.every((i) => i.componentName_voId !== l.componentName_voId));
        removeList.forEach((l) => {
          const deleteRequest = store.delete(l.componentName_voId);
          deleteRequest.onsuccess = () => {
            console.log('delete');
          };
        });
      });

      tx.oncomplete = () => {
        resolve({
          code: 200,
          msg: '更新成功',
        });
      };
    });
  }

  private _searchByComponentName(componentName: string, store: any): Promise<IData[]> {
    return new Promise((resolve) => {
      const index = store.index('componentName');
      const getRequest = index.openCursor(IDBKeyRange.only(componentName));
      const result: IData[] = [];

      getRequest.onsuccess = (e) => {
        const cursor = e.target.result;

        if (cursor) {
          result.push(cursor.value);
          cursor.continue();
        } else {
          // 没有更多数据
          resolve(result);
        }
      };
    });
  }

  public searchByComponentName(componentName: string): Promise<IData[]> {
    const tx = this.connection.transaction('voStore', 'readwrite');
    const store = tx.objectStore('voStore');

    return new Promise((resolve) => {
      const index = store.index('componentName');
      const getRequest = index.openCursor(IDBKeyRange.only(componentName));
      const result: IData[] = [];

      getRequest.onsuccess = (e) => {
        const cursor = e.target.result;

        if (cursor) {
          result.push(cursor.value);
          cursor.continue();
        } else {
          // 没有更多数据
          resolve(result);
        }
      };
    });
  }
}

export default Database;

Reference #

https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API

...

网站内容防复制及追踪

I. 引言:网站内容保护的挑战在 #

数字时代,在线内容的价值日益凸显,但同时也面临着前所未有的脆弱性。数字信息的便捷性使得复制和传播变得轻而易举,这给内容创作者带来了知识产权盗窃、抄袭和收入损失等诸多问题。因此,网站所有者和内容创建者越来越关注如何有效地保护其在线内容。本文旨在探讨防止网站用户复制内容以及追踪此类尝试的各种方法。

II. 复制预防的技术实现 #

A. 使用 CSS 禁用文本选择 #

body {
  user-select: none;
}

虽然 JavaScript 也可以用于禁用文本选择(通过处理 selectstart 事件),但通常首选 CSS 方法,因为它更简单且效率更高。值得注意的是,即使使用 CSS 阻止了视觉上的文本选择,底层的文本内容仍然可能通过编程方式被访问。这意味着基于 CSS 的预防主要是针对普通用户的威慑,而不是一种强大的安全措施。

B. 使用 JavaScript 阻止右键单击上下文菜单 #

阻止用户右键单击网页是另一种常见的防止内容复制的技术。JavaScript 提供了多种方法来实现这一点。最常用的方法是使用 contextmenu 事件。当用户尝试右键单击页面上的某个元素时,会触发此事件。通过 JavaScript 拦截此事件并阻止其默认行为(显示上下文菜单),可以有效地禁用右键单击功能。

document.addEventListener("contextmenu", (event) => event.preventDefault());

此外,还可以使用 oncontextmenu HTML 属性直接在 HTML 元素中禁用右键单击,例如:

<div oncontextmenu="return false;"></div>

虽然这种方法更直接,但不如使用 addEventListener 灵活。addEventListener 通常被认为是处理 DOM 事件的更现代和更灵活的方式。

值得一提的是,CSS 也可以通过 pointer-events: none; 属性禁用右键单击功能。然而,这种方法的一个主要缺点是它也会禁用所有其他指针交互,例如点击,这在许多情况下是不可取的。因此,对于禁用右键单击上下文菜单,JavaScript 通常是更实用的选择。

阻止右键单击通常被用作防止用户轻松访问文本和图像的"复制"选项的一种手段。

C. 阻止键盘复制快捷键(Ctrl+C 等) #

除了禁用文本选择和右键单击之外,还可以使用 JavaScript 来尝试阻止用户使用键盘快捷键复制内容,例如 Ctrl+C(在 Windows 上)或 Command+C(在 macOS 上)。这通常通过监听 keydownkeyup 事件来实现。

...

Hugo

Hugo Introduction #

Overview #

Hugo is a static site generator written in Go, optimized for speed and designed for flexibility. With its advanced templating system and fast asset pipelines, Hugo renders a complete site in seconds, often less.

Due to its flexible framework, multilingual support, and powerful taxonomy system, Hugo is widely used to create:

  • Corporate, government, nonprofit, education, news, event, and project sites
  • Documentation sites
  • Image portfolios
  • Landing pages
  • Business, professional, and personal blogs
  • Resumes and CVs

Hugo’s fast asset pipelines include:

...

Hugo 概述


一、Hugo框架概述 #

Hugo是由Go语言编写的静态网站生成器(Static Site Generator, SSG),以极快的构建速度简洁的架构著称。它的核心设计理念是:

  • 无需数据库:所有内容基于Markdown文件。
  • 零依赖:仅需一个二进制文件即可运行。
  • 高性能:构建数千页面仅需毫秒级时间。

适用场景:技术文档、博客、企业官网、产品展示页等。


二、Hugo核心特性 #

1. 极速构建 #

  • 基于Go语言的并发编译机制,构建速度远超Jekyll、Hexo等工具。
  • 示例:构建1000个页面仅需约100ms。

2. 跨平台支持 #

  • 支持Windows、macOS、Linux,仅需一个二进制文件即可运行。

3. 灵活的内容管理 #

  • 支持Markdown、Org-mode、HTML等多种格式。
  • Front Matter:通过YAML/TOML/JSON定义页面元数据(如标题、日期、分类等)。

4. 强大的模板引擎 #

  • 基于Go语言的html/template库,支持逻辑控制、变量注入、模板继承等。
  • 短代码(Shortcodes):在Markdown中嵌入可复用的HTML组件。

5. 主题系统 #

  • 社区提供超过400个主题(如Ananke、DocDock),支持一键安装。
  • 可自定义模板覆盖(Template Overrides)。

6. 多语言支持 #

  • 原生支持国际化(i18n),轻松创建多语言站点。
  • 通过config.toml配置语言参数。

7. 实时热重载(Live Reload) #

  • 开发模式下修改内容后,浏览器自动刷新。

三、Hugo安装与配置 #

1. 安装方法 #

  • macOSbrew install hugo
  • Linuxsnap install hugo
  • Windowschoco install hugo 或直接下载二进制文件。

验证安装:

...

Hugo 语法

Hugo的语法主要涉及 模板引擎内容格式(Front Matter)短代码(Shortcodes),以下是详细的语法介绍:


一、Front Matter:内容的元数据 #

Front Matter是Hugo内容文件顶部的元数据块,用于定义页面的标题、日期、分类等属性,支持 YAMLTOMLJSON 三种格式。

1. YAML格式 #

---包裹:

---
title: "Hello Hugo"
date: 2023-10-01
tags: ["SSG", "Hugo"]
draft: false
---

2. TOML格式 #

+++包裹:

+++
title = "Hello Hugo"
date = 2023-10-01T15:00:00+08:00
tags = ["SSG", "Hugo"]
draft = false
+++

3. JSON格式 #

{ ... }包裹:

{
  "title": "Hello Hugo",
  "date": "2023-10-01",
  "tags": ["SSG", "Hugo"],
  "draft": false
}

常用字段 #

  • title: 页面标题
  • date: 发布日期(支持时间戳)
  • draft: 是否为草稿(true时默认不生成页面)
  • tags/categories: 标签和分类(支持多级分类)
  • weight: 控制页面在列表中的排序权重
  • aliases: 页面别名(用于重定向)

二、Go模板语法 #

Hugo使用Go语言的 html/template 引擎,支持逻辑控制、变量、函数等。

...

Software Engineering

软件工程 - 敏捷过程 #

Review

  1. 2020/04/08

时间就像一张网,你撒在哪里,你的收获就在哪里。

我们一直在实践中探寻更好的软件开发方法,身体力行的同时也帮助他人。由此我们建立了如下价值观: 个体和互动 高于 流程和工具 工作的软件 高于 详尽的文档 客户合作 高于 合同谈判 响应变化 高于 遵循计划 也就是说,尽管右项有其价值,我们更重视左项的价值。

不是我们流程完全按照敏捷设计了就是敏捷了,而是需要不断反思我们是否按照这个思想在实践了才是符合敏捷思想的核心。 换一个角度,其实和我们组织的价值观是一致的。

  • 主人翁意识;
  • 追求卓越;
  • 空杯心态;
  • 思变创新;
  • 以客户为中心;
  • 真诚直接

敏捷相关的知识点 1、敏捷核心价值观 2、完整的scrum流程 3、每日例会和看板 4、回顾例会

软件过程模型 #

  • 瀑布模型
  • 快速原型模型
  • 增量模型
  • 螺旋模型
  • 喷泉模型
  • RUP
  • 敏捷过程
  • 微软过程

软件工程分析 #

  1. 问题定义
  2. 可行性研究
  3. 需求分析
  4. 总体设计
  5. 详细设计
  6. 编码和单元测试
  7. 综合测试
  8. 软件维护