首页 公告 项目 RSS

linkwarden搭建和使用

March 20, 2024 本文有 1753 个字 需要花费 4 分钟阅读

  1. 简介

作为一个热衷于阅读和收集网络资源的人,我经常面临着管理大量URL的问题。过去我一直使用TickTick来管理这些URL,但是随着时间的推移,有些链接可能会失效,这是互联网的常态。很久以前我就知道了Linkwarden这个项目,但那时候这个项目并不成熟。最近我重新审视了这个项目,发现它已经足够成熟,支持浏览器扩展,iOS上支持快捷指令导入URL,支持PWA,几乎可以说是完美的解决方案。

搭建postgresql

Linkwarden使用PostgreSQL作为数据库存储。我通常会将其部署在Kubernetes环境中,下面是一个PostgreSQL的Kubernetes配置示例:

首先是sts.yaml

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgresql
  namespace: app
spec:
  selector:
    matchLabels:
      app: postgresql
  serviceName: postgresql
  replicas: 1
  template:
    metadata:
      labels:
        app: postgresql
    spec:
      hostNetwork: true
      containers:
      - name: postgresql
        image: postgres:14.11-alpine3.19
        ports:
        - containerPort: 5432
          name: postgresql
        env:
        - name: POSTGRES_PASSWORD
          value: 'xxxxxx'
        - name: POSTGRES_HOST_AUTH_METHOD
          value: trust
        volumeMounts:
        - name: postgresql-data
          mountPath: /var/lib/postgresql/data
        - name: timezone
          mountPath: /etc/localtime
          readOnly: true
      volumes:
        - name: timezone
          hostPath: 
            path: /usr/share/zoneinfo/Asia/Shanghai  
  volumeClaimTemplates:
  - metadata:
      name: postgresql-data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 200Gi

然后是svc.yaml

apiVersion: v1
kind: Service
metadata:
  name: postgresql
  namespace: app
spec:
  selector:
    app: postgresql
  ports:
  - port: 5432
    targetPort: 5432

搭建linkwarden

接下来是Linkwarden的Kubernetes配置示例:

首先是sts.yaml

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: linkwarden
  namespace: app
spec:
  selector:
    matchLabels:
      app: linkwarden
  serviceName: linkwarden
  replicas: 1
  template:
    metadata:
      labels:
        app: linkwarden
    spec:
      containers:
      - name: linkwarden
        image: ghcr.io/linkwarden/linkwarden:v2.5.1
        ports:
        - containerPort: 3000
          name: linkwarden
        env:
        - name: NEXTAUTH_SECRET
          value: 'xxxxxxxxxxxxxxx'
        - name: NEXTAUTH_URL
          value: https://linkwarden.your-domain.com/api/v1/auth
        - name: POSTGRES_PASSWORD
          value: 'xxxxxxxxxxxxxxxx'
        - name: DATABASE_URL
          value: postgresql://postgres:${POSTGRES_PASSWORD}@postgresql:5432/linkwarden
        volumeMounts:
        - name: linkwarden-data
          mountPath: /data/data
        - name: timezone
          mountPath: /etc/localtime
          readOnly: true
      volumes:
        - name: timezone
          hostPath: 
            path: /usr/share/zoneinfo/Asia/Shanghai  
  volumeClaimTemplates:
  - metadata:
      name: linkwarden-data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 200Gi

环境变量说明:

  • NEXTAUTH_SECRET: 一个随机字符串,了解我的人都知道我会随机创建 一个文件夹,然后获取他的md5作为这个随机字符串
  • NEXTAUTH_URL: linkwarden的访问url
  • POSTGRES_PASSWORD: pg数据库的密码
  • DATABASE_URL: 数据库链接地址

其他的环境变量可以参考Linkwarden官方文档:

https://docs.linkwarden.app/self-hosting/environment-variables

Linkwarden还支持多种第三方登录方式,具体可以参考:

https://docs.linkwarden.app/self-hosting/sso-oauth

然后是svc.yaml

apiVersion: v1
kind: Service
metadata:
  name: linkwarden
  namespace: app
spec:
  selector:
    app: linkwarden
  ports:
  - port: 3000
    targetPort: 3000

最后是ingress.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: linkwarden-ingress
  namespace: app
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/ssl-passthrough: "true"
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
spec:
  rules:
  - host: "your-domain.com"
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: linkwarden
            port:
              number: 3000
  tls:
  - hosts:
    - your-domain.com
    secretName: your-tls-secret

从ticktick导出数据

由于我收集的URL数量接近800个,手动添加显然是不切实际的。因此,我需要先从TickTick获取历史数据。TickTick并没有提供数据导出功能,但通过分析网络请求,我发现使用以下URL可以获取所有数据:

https://api.ticktick.com/api/v2/batch/check/0

返回的数据是JSON格式,其中projectProfiles键包含清单的详细信息,syncTaskBean键包含任务的详细信息。如果你想获取特定清单下的所有数据,只需将syncTaskBean中的JSON数据转换成CSV格式,然后使用Excel筛选projectId字段即可。projectProfiles中的id就是syncTaskBean中的projectId

筛选完成后,获取所有的titlecontent字段的内容,使用URL提取工具(如:https://www.67tool.com/text/extract-urls)即可获取所有的URL。

导入数据到linkwarden

接下来就是导入数据的步骤。我编写了一个Python脚本来完成这个任务。首先,创建一个urls.txt文件,然后将刚才获取的所有URL按行写入。登录Linkwarden,获取你登录后的cookie,修改下面脚本中的cookie和对应的Linkwarden地址,然后运行脚本即可。

import requests
import json

# 读取文本文件中的URL
with open('urls.txt', 'r') as file:
    urls = file.readlines()

# 设置请求头
headers = {
    'authority': 'your-linkwarden-domain',
    'accept': '*/*',
    'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8',
    'content-type': 'application/json',
    'cookie': 'your-cookie', # 填入你的cookie
    'origin': 'https://your-linkwarden-domain',
    'referer': 'https://your-linkwarden-domain/dashboard',
    'sec-ch-ua': '"Chromium";v="122", "Not(A:Brand";v="24", "Google Chrome";v="122"',
    'sec-ch-ua-mobile': '?0',
    'sec-ch-ua-platform': '"macOS"',
    'sec-fetch-dest': 'empty',
    'sec-fetch-mode': 'cors',
    'sec-fetch-site': 'same-origin',
    'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36'
}

# 遍历URL,发送POST请求
for url in urls:
    url = url.strip()
    data = {
        "name": "",
        "url": url, # 导入的url
        "description": "",
        "type": "url",
        "tags": [],
        "preview": "",
        "image": "",
        "pdf": "",
        "readable": "",
        "textContent": "",
        "collection": {
            "id": "your-collection-id", # 这里是你linkwarden的collection的id
            "name": "your-collection-name", # 这里是你linkwarden的collection名字
            "ownerId": "your-user-id" # 这里是你linkwarden的用户id
        }
    }

    response = requests.post('https://your-linkwarden-domain/api/v1/links', headers=headers, data=json.dumps(data))

    # 检查响应状态
    if response.status_code == 200:
        print(f'Successfully posted URL: {url}')
    else:
        print(f'Failed to post URL: {url}. Status code: {response.status_code}')

注意替换脚本中的your-linkwarden-domainyour-cookie,your-collection-id,your-user-idyour-collection-name为你实际的值。

这样,所有的URL就都成功导入到Linkwarden中了。

欢迎关注我的博客www.bboy.app

Have Fun