bitwarden 简称 bw, vaultwarden 简称 vw
思路:
1, 先将 bw 的数据导出成 bw.json.
2, 将 bw.json 导入你自建的 vw.
3, 将你自建的 vw 也导出一个 vw.json
4, 把 vaultwarden 的 docker / 主程序 停止, 启动, 再停止, 以保证 sqlite wal 全部写入数据库 db.sqlite3 文件.
5, 可以把 db.sqlite3 文件下载到本地, 用 navicat 或者 dbeaver 等工具打开, 这时你看到 vw 的 sqlite 数据中, created_at updated_at 全部变成了清一色导入的时间, 奇葩行为..
5, 使用 ai 生成代码, 寻找两个 json 中 id 的对应关系, id 是不同的, 如何一一对应呢?
我使用的是 name + username 的组合对应关系. 除此之外, 当没有 username 的时候(银行卡类型), 用 name + notes 来组合, 以防止重复的情况.
也就是:
两个不同的数据库 json 导出文件内容相同, id 不同, bw.json 的两个时间 creationDate 和 revisionDate 是对的, vw.json 中的两个时间是错的. 通过 name+username (没有 username 就用 name+notes) 来找到两个 json 的 "id" 对应关系.
随后根据这个 id 的对应关系, 将 bw.json 中的 creationDate 和 revisionDate 读取出来, 写到数据库对应 id 中的 created_at 和 updated_at 中去.
6, 我使用 ChatGPT 生成的代码如下:
<?php
$bwFile = 'bw.json';
$vwFile = 'vw.json';
$dbFile = 'db.sqlite3';
$bwJson = json_decode(file_get_contents($bwFile), true);
$vwJson = json_decode(file_get_contents($vwFile), true);
if (!$bwJson || !$vwJson) {
die("JSON 解析失败\n");
}
$bwItems = $bwJson['items'] ?? [];
$vwItems = $vwJson['items'] ?? [];
/**
* key = name + username/notes
*/
function buildKey($item) {
$name = $item['name'] ?? '';
$username = $item['login']['username'] ?? null;
$notes = $item['notes'] ?? '';
$second = $username;
if ($second === null || $second === '') {
$second = $notes;
}
return $name . '|' . $second;
}
/**
* SQLite connect
*/
$pdo = new PDO("sqlite:" . $dbFile);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
/**
* =======================
* 1. BW index
* =======================
*/
$bwMap = [];
foreach ($bwItems as $item) {
if (!isset($item['name'])) continue;
$key = buildKey($item);
$bwMap[$key] = [
'creationDate' => $item['creationDate'] ?? null,
'revisionDate' => $item['revisionDate'] ?? null,
];
}
/**
* =======================
* 2. Update DB
* =======================
*/
$updateStmt = $pdo->prepare("
UPDATE ciphers
SET created_at = :created_at,
updated_at = :updated_at
WHERE uuid = :uuid
");
$updated = 0;
$skipped = 0;
$skippedList = [];
foreach ($vwItems as $item) {
if (!isset($item['name'], $item['id'])) {
$skipped++;
$skippedList[] = [
'reason' => 'missing name or id',
'item' => $item
];
continue;
}
$key = buildKey($item);
if (!isset($bwMap[$key])) {
$skipped++;
$skippedList[] = [
'reason' => 'bw not found',
'id' => $item['id'],
'key' => $key
];
continue;
}
$bw = $bwMap[$key];
if (empty($bw['creationDate']) || empty($bw['revisionDate'])) {
$skipped++;
$skippedList[] = [
'reason' => 'missing bw dates',
'id' => $item['id'],
'key' => $key,
'bw' => $bw
];
continue;
}
try {
$updateStmt->execute([
':created_at' => $bw['creationDate'],
':updated_at' => $bw['revisionDate'],
':uuid' => $item['id'],
]);
$updated++;
} catch (Exception $e) {
$skipped++;
$skippedList[] = [
'reason' => 'sql error',
'id' => $item['id'],
'error' => $e->getMessage()
];
}
}
/**
* =======================
* 3. Output
* =======================
*/
echo "UPDATED: {$updated}\n";
echo "SKIPPED: {$skipped}\n";
echo "\n=== SKIPPED DETAILS ===\n";
if (empty($skippedList)) {
echo "NONE\n";
} else {
foreach ($skippedList as $s) {
echo json_encode($s, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) . "\n";
}
}
运行代码之前, 要把 php.ini 中的两个插件启用, 也就是把这两行前面的分号去掉, 然后重启 php:
extension=pdo_sqlite
extension=sqlite3
如果你的 extensions 里面甚至都没有这两个 dll, 那么改 php.ini 也没有用了, 推荐使用类似 wamp 的一键端, 它有很多名字: uniform server, unicontroller, miniserver, 都是同一个软件. 内含 apache mysql php, 只有 php 就行. 链接: https://sourceforge.net/projects/miniserver/
最终输出如下:
UPDATED: 成功的数量 SKIPPED: 1 === SKIPPED DETAILS === { "reason": "bw not found", "id": "这里是uuid序号", "key": "这里是json里的name字段|这里是json里的username字段" }
没找到就说明 bw 里面没有, 这是你自建了以后新增的记录.
再去看看数据库里面的 created_at 和 updated_at 已经写好了, 但是格式不对, 简单替换下, sql 执行:
UPDATE ciphers
SET
created_at = REPLACE(REPLACE(created_at, 'T', ' '), 'Z', ''),
updated_at = REPLACE(REPLACE(updated_at, 'T', ' '), 'Z', '');
0 条评论:
发表评论