首页 > 其他分享 >从腾讯云开发迁移到 Laf 完全指南!

从腾讯云开发迁移到 Laf 完全指南!

时间:2023-09-15 21:35:58浏览次数:45  
标签:指南 const Laf await db collection 腾讯 data DbName

laf背景概述

目前,我的小程序运行在腾讯云 Cloudbase 平台上,采用基础套餐+按量付费的计费方式。虽然每月费用大约在 100 元左右,但我认为这是不必要的。此前由于时间紧迫,我并未对腾讯云的优惠政策进行深入了解。在与@白夜讨论了计费规则后,我对收费机制有了更清晰的认识。另外,由于对@米开朗基杨大神的信任,我开始考虑迁移项目。感谢 Laf 提供的免费试用时长,让我有足够的时间进行测试。

laf应对挑战

我的小程序功能较为简单,包括几个云函数和 4 个集合。其中两个集合的数据量较大,约为 200M。最后,我使用新的云函数实现了旧功能的支持。以下是我在迁移过程中需要解决的问题:

  1. 如何实现用户登录状态的保持?

  2. 如何进行云数据的迁移?

  3. 如何创建云数据库索引?

  4. 如何发布新版本?

迁移方案

针对上述挑战,我制定了以下解决方案:

用户登录状态保持

以下是云函数代码:

import cloud from "@lafjs/cloud";
import { createHash } from "crypto";

exports.main = async function (ctx: FunctionContext) {
  const username = ctx.body?.username || "";
  const password = ctx.body?.password || "";

  // check user login
  const db = cloud.database();
  const res = await db
    .collection("users")
    .where({
      username: username,
      password: createHash("sha256").update(password).digest("hex"),
    })
    .getOne();

  if (!res.data) return { error: "invalid username or password" };

  // generate jwt token
  const user_id = res.data._id;
  const payload = {
    uid: user_id,
    exp: Math.floor(Date.now() / 1000) + 60 * 60 * 24 * 7,
  };

  const access_token = cloud.getToken(payload);

  return {
    uid: res.data._id,
    access_token: access_token,
  };
};

这段代码使用了 JWT 进行登录验证,返回的 access_token 作为登录信息凭证。需要注意的是,为了避免伪造 token,可以在应用设置-> 环境变量中设置 SERVER_SECRET

云数据迁移

为了迁移云数据,我参考了一个 Laf 备份迁移工具——GitHub - nightwhite/BackupLaf。我使用此工具将腾讯云的数据以 JSON 格式存储,然后将文件存储到 Laf 的 OSS 中。最后,我用云函数将数据导入到 Laf 数据库。

以下是我使用的代码:

备份部分

const tcb = require('@cloudbase/node-sdk');
const fs = require('fs');

// 1. 直接使用下载的私钥文件
const app = tcb.init({
    env: '<env>', // 您的环境id
    secretId: '<secretId>',
    secretKey: '<secretKey>',
    credentials: require('./config/tcb_custom_login_key.json'),    // 直接使用下载的私钥文件
});

const BackupDBPath = "out"
const DbName = "<DbName>"
const pageSize = 1000;

const db = app.database();

async function load_data() {
    const count = await db.collection(DbName).count();
    let total = count.total
    // 1. 分页;
    //计算需分几次取
    const batchTimes = Math.ceil(total / pageSize);
    //批量获取数据
    let start = 0
    //如果查询到批次
    const batchRes = await db.collection("BackupDB").where({DbName: DbName}).orderBy('createAt', 'desc').limit(1).get()
    if (batchRes.data[0]) {
        start = batchRes.data[0].Batch
    }
    let dbInfo = {}
    dbInfo[DbName] = total
    for (let i = start; i < batchTimes; i++) {
        console.log(`Begin loading page: ${i}/${batchTimes}`)
        try {
            const res = await db.collection(DbName).skip(i * pageSize).limit(pageSize).get();
            const filename = `${BackupDBPath}/${DbName}/${i}.json`
            const stream = fs.createWriteStream(filename);
            stream.write(JSON.stringify(res.data));
            stream.end();

            stream.on('finish', () => {
                console.log(`Data written to ${filename} successfully!`);
            });
            // 记录插入表的批次,保存到数据库
            console.log(`插入 ${DbName}表第 ${i}批数据成功`);

            const batchRes = await db.collection("BackupDB").where({DbName: DbName}).orderBy('createAt', 'desc').limit(1).get()
            if (!batchRes.data[0]) {
                await db.collection("BackupDB").add({
                    DbName: DbName,
                    Batch: i,
                    pageSize: pageSize,
                    createAt: new Date()
                })
            } else {
                await db.collection("BackupDB").where(
                    {
                        DbName: DbName,
                    }
                ).update({
                    DbName: DbName,
                    Batch: i,
                    pageSize: pageSize,
                    createAt: new Date()
                })
            }
        } catch (error) {
            console.log(error);
            return {data: "备份出错:" + error};
        }
    }

    try {
        const filename = `${BackupDBPath}/${DbName}/info.json`
        const stream = fs.createWriteStream(filename);
        stream.write(JSON.stringify(dbInfo));
        stream.end();
        stream.on('finish', () => {
            console.log(`Data written to ${filename} successfully!`);
        });
    } catch (error) {
        console.log(error);
        return {data: "备份出错:" + error};
    }
}

load_data()

将集合数据以 JSON 数组形式存入本地文件夹。尝试了一下,我的 pagesize 设置 5000 好像还不行,所以最后还是 1k 每页的数据存储的。

将文件推送到 Laf 虽然 Laf 提供文件夹上传,但是我有个集合存了 122w 的数据,也就是说光 JSON 文件就 1k 多个,用网页上传实在是慢,研究了下用本地代码上传。

先用云函数获取 STS 的秘钥:生成云存储临时令牌 (STS) | Laf 云开发,代码什么不用改,搞上去调用即可。然后上传文件:

const dirName = '<dirName>'
const filePath = `./out/${dirName}`
const BUCKET_PATH_PREFIX = `BackupDB/${dirName}`
const APPID = "<appid>"; // Laf 应用 appid

async function upload_files() {
    new Cloud({
        baseUrl: `https://${APPID}.laf.run`,
        getAccessToken: () => "",
    });
    //  sts 生成的秘钥部分,贴进来
    const sts = {
        "credentials": {
            "AccessKeyId": "<AccessKeyId>",
            "SecretAccessKey": "<SecretAccessKey>",
            "SessionToken": "<SessionToken>",
            "Expiration": "2023-05-06T21:19:02.000Z"
        },
        "endpoint": "https://oss.laf.run",
        "region": "cn-hz"
    }

    const s3Client = new S3({
        endpoint: sts.endpoint,
        region: sts.region,
        credentials: {
            accessKeyId: sts.credentials.AccessKeyId,
            secretAccessKey: sts.credentials.SecretAccessKey,
            sessionToken: sts.credentials.SessionToken,
            expiration: sts.credentials.Expiration,
        },
        forcePathStyle: true,
    });


    // 遍历文件夹 filePath
    const files = fs.readdirSync(filePath);
    for (let i = 0; i < files.length; i++) {
        const filename = path.join(filePath, files[i]);
        const fileStream = fs.createReadStream(filename);
        fileStream.on('error', function (err) {
            console.log('File Error', err);
        });

       // bucket name prefixed with appid
        const bucket = `${APPID}-<bucket name>`;
        const cmd = new PutObjectCommand({
            Bucket: bucket,
            Key: `${BUCKET_PATH_PREFIX}/${files[i]}`,
            // Body: "Hello from laf oss!", // 文件内容可以是二进制数据,也可以是文本数据,或者是 File 对象
            Body: fileStream,
            ContentType: "application/json",
        });
        const res = await s3Client.send(cmd);
        console.log(res);
    }
}

upload_files()

还原数据

数据还原部分参考了

标签:指南,const,Laf,await,db,collection,腾讯,data,DbName
From: https://blog.51cto.com/u_16255648/7486743

相关文章

  • 网络工程师的甩锅指南,果断收藏
    大家好,我是老杨。都说IT行业最容易被甩锅的就是网工,这是有科学依据的,比如:纵观我网工群的群友聊天,“锅”不离口,很难不说明一点什么问题。遇到甩锅,我相信没有哪位朋友的心情是愉悦的。到底怎么避免甩锅,或许你要从最源头开始追溯本源,找到根本解决之道。今天就来和你聊上一聊。今日文章......
  • OpenHarmony系统能力SystemCapability使用指南
    一、概述1、系统能力与 APISysCap,全称SystemCapability,即系统能力,指操作系统中每一个相对独立的特性,如蓝牙,WIFI,NFC,摄像头等,都是系统能力之一。每个系统能力对应多个API,随着目标设备是否支持该系统能力共同存在或消失,也会随着DevEco Studio一起提供给开发者做联想。开发者可以在[S......
  • Nutch相关框架安装使用最佳指南(转)
    Chineseinstallingandusinginstruction - Thebestguidanceininstallingandusing NutchinChina 国内首套免费的《Nutch相关框架视频教程》        土豆在线观看地址:http://www.tudou.com/home/item_u106249539s0p1.html 超清原版下载地址: http://pan.......
  • 软件测试|深入解析Docker Run命令:创建和启动容器的完全指南
    简介Docker是一种流行的容器化平台,用于构建、分发和运行应用程序。其中一个最基本且重要的Docker命令是dockerrun,用于创建和启动容器。本文将详细解析dockerrun命令的用途、参数和示例,帮助您全面掌握创建和启动容器的过程。dockerrun在Docker中,容器是运行应用程序的独立环......
  • 软件测试|使用PyMySQL访问MySQL数据库的详细指南
    简介PyMySQL是Python中流行的MySQL数据库驱动程序,它提供了便捷的方法来连接、查询和更新MySQL数据库。本文将为您提供使用PyMySQL访问MySQL数据库的详细指南,包括安装PyMySQL、连接数据库、执行查询和更新操作等。获取更多技术资料,请点击!环境准备在开始之前,您需要确保已经安装......
  • 软件测试|Docker Kill/Pause/Unpause命令详细使用指南
    简介Docker是一种流行的容器化平台,提供了各种命令和功能来管理和操作容器。本文将详细介绍Docker中的三个重要命令:kill、pause和unpause。我们将深入了解它们的作用、用法和示例,帮助您更好地理解和使用这些命令。什么是DockerKill/Pause/Unpause命令?Docker提供了几个与容器生......
  • 软件测试|Docker exec命令详细使用指南
    简介Dockerexec命令是Docker提供的一个强大工具,用于在正在运行的容器中执行命令。本文将详细介绍Dockerexec命令的用法和示例,帮助大家更好地理解和使用这个命令。Docker是一种流行的容器化平台,允许我们在容器中运行应用程序。有时候,在容器内执行命令可以帮助我们调试、排查问......
  • 软件测试|Python Selenium 库安装使用指南
    简介Selenium是一个用于自动化浏览器操作的强大工具,它可以模拟用户在浏览器中的行为,例如点击、填写表单、导航等。在本指南中,我们将详细介绍如何安装和使用Python的Selenium库。安装Selenium库使用以下命令可以通过pip安装Selenium库:pipinstallselenium安装WebDriver......
  • 「Java开发指南」在MyEclipse中的Spring开发(二)
    在上文中(点击这里回顾>>),我们主要介绍了一些Spring的基本概念、Spring项目配置及向导,本章节将继续介绍如何管理多个项目,Spring配置编辑器等,欢迎持续关注~MyEclipsev2023.1.2离线版下载MyEclipse技术交流群:742336981欢迎一起进群讨论4.管理多个Beans和项目除了使用bean配置文......
  • 续集来了!我让 GPT-4 用 Laf 三分钟写了个完整的待办事项 App
    一觉醒来,GPT-4已经发布了!GPT-4实现了真正的多模态,可以把纸笔画的原型直接写出网页代码。读论文时还能理解插图含意。好消息是,ChatGPTPlus用户目前可以提前尝鲜GPT-4模型。作为高贵的Plus用户,这怎么能忍?立马打开ChatGPT切换到最新模型。一位Twitter网友经过测试发现......