laf背景概述
目前,我的小程序运行在腾讯云 Cloudbase 平台上,采用基础套餐+按量付费的计费方式。虽然每月费用大约在 100 元左右,但我认为这是不必要的。此前由于时间紧迫,我并未对腾讯云的优惠政策进行深入了解。在与@白夜讨论了计费规则后,我对收费机制有了更清晰的认识。另外,由于对@米开朗基杨大神的信任,我开始考虑迁移项目。感谢 Laf 提供的免费试用时长,让我有足够的时间进行测试。
laf应对挑战
我的小程序功能较为简单,包括几个云函数和 4 个集合。其中两个集合的数据量较大,约为 200M。最后,我使用新的云函数实现了旧功能的支持。以下是我在迁移过程中需要解决的问题:
-
如何实现用户登录状态的保持?
-
如何进行云数据的迁移?
-
如何创建云数据库索引?
-
如何发布新版本?
迁移方案
针对上述挑战,我制定了以下解决方案:
用户登录状态保持
以下是云函数代码:
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