diff --git a/show_all.js b/show_all.js new file mode 100644 index 00000000..1b720ee9 --- /dev/null +++ b/show_all.js @@ -0,0 +1,146 @@ +#!/usr/bin/env node +const fs = require('fs'); +const https = require('https'); + +const CONFIG_PATH = process.env.HOME + '/.config/vaultwarden/config.json'; +let config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8')); + +const VAULT_URL = config.url; +const CLIENT_ID = config.client_id; +const CLIENT_SECRET = config.client_secret; +let token = null; +let tokenExpiry = null; +const deviceId = 'openclaw-cli-' + require('crypto').randomUUID(); + +async function getToken() { + if (token && tokenExpiry && Date.now() < tokenExpiry - 300000) { + return token; + } + + const params = new URLSearchParams({ + grant_type: 'client_credentials', + scope: 'api', + client_id: CLIENT_ID, + client_secret: CLIENT_SECRET, + device_identifier: deviceId, + device_type: '14', + device_name: 'OpenClaw CLI' + }); + + const response = await new Promise((resolve, reject) => { + const url = new URL(VAULT_URL + '/identity/connect/token'); + const options = { + hostname: url.hostname, port: url.port || 443, + path: url.pathname + url.search, method: 'POST', + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + timeout: 30000 + }; + + const req = https.request(options, (res) => { + let body = ''; + res.on('data', chunk => body += chunk); + res.on('end', () => { + try { + const json = JSON.parse(body); + resolve(json); + } catch (e) { reject(e); } + }); + }); + req.on('error', reject); + req.write(params.toString()); + req.end(); + }); + + if (!response.access_token) { + throw new Error('认证失败'); + } + + token = response.access_token; + tokenExpiry = Date.now() + ((response.expires_in || 7200) - 300) * 1000; + return token; +} + +async function getItem(id) { + const t = await getToken(); + return new Promise((resolve, reject) => { + const url = new URL(VAULT_URL + `/api/ciphers/${id}`); + const options = { + hostname: url.hostname, port: url.port || 443, + path: url.pathname + url.search, method: 'GET', + headers: { 'Authorization': `Bearer ${t}` }, + timeout: 30000 + }; + + const req = https.request(options, (res) => { + let body = ''; + res.on('data', chunk => body += chunk); + res.on('end', () => { + try { + const json = JSON.parse(body); + resolve(json); + } catch (e) { reject(e); } + }); + }); + req.on('error', reject); + req.end(); + }); +} + +async function getAllItems() { + const t = await getToken(); + const response = await new Promise((resolve, reject) => { + const url = new URL(VAULT_URL + '/api/ciphers'); + const options = { + hostname: url.hostname, port: url.port || 443, + path: url.pathname + url.search, method: 'GET', + headers: { 'Authorization': `Bearer ${t}` }, + timeout: 30000 + }; + + const req = https.request(options, (res) => { + let body = ''; + res.on('data', chunk => body += chunk); + res.on('end', () => { + try { + const json = JSON.parse(body); + resolve(json); + } catch (e) { reject(e); } + }); + }); + req.on('error', reject); + req.end(); + }); + + return response.data || []; +} + +async function main() { + try { + const items = await getAllItems(); + console.log(`\n📋 密码列表 (${items.length})\n`); + console.log('=' .repeat(70)); + + for (const item of items) { + const detail = await getItem(item.id); + console.log(`\n🔐 ${detail.name}`); + console.log('-'.repeat(50)); + console.log(` ID: ${detail.id}`); + if (detail.login?.username) console.log(` 用户名: ${detail.login.username}`); + if (detail.login?.password) console.log(` 密码: ${detail.login.password}`); + if (detail.login?.uris?.length > 0) { + detail.login.uris.forEach(uri => { + console.log(` URL: ${uri.uri}`); + }); + } + if (detail.notes) console.log(` 备注: ${detail.notes}`); + } + + console.log('\n' + '='.repeat(70)); + console.log(`\n✅ 共 ${items.length} 个密码\n`); + } catch (error) { + console.error('\n❌ 错误:', error.message); + process.exit(1); + } +} + +main(); diff --git a/vaultwarden_backup.md b/vaultwarden_backup.md new file mode 100644 index 00000000..b424d76e --- /dev/null +++ b/vaultwarden_backup.md @@ -0,0 +1,118 @@ +# Vaultwarden 密码库备份 +# 生成时间: 2026-02-13 22:14 + +--- + +## 坚果云 WebDAV +- **用户名:** work_fyx02@outlook.com +- **密码:** auyqxhk7fvhvhh3w +- **备注:** WebDAV password for jianguoyun.com + +--- + +## Gitea Token +- **用户名:** fyx +- **密码:** 7840e0250de4cf994631b1eadb4fc469947aa7df +- **备注:** API Token for Gitea (162.211.228.232:8418) + +--- + +## FreshRSS API +- **用户名:** 1803560007 +- **密码:** asCdEfGhsasdasdavWxYz1234ass +- **备注:** API Token for FreshRSS (43.163.195.176:36847) + +--- + +## 飞牛 NAS +- **用户名:** fyx +- **密码:** fengyaxing123 +- **备注:** WebDAV for Feiniu NAS (f180356.5ddd.com) + +--- + +## GitHub PAT +- **用户名:** 1803560007 +- **密码:** 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 +- **备注:** Personal Access Token for GitHub + +--- + +## YouTube API +- **用户名:** API Key +- **密码:** AIzaSyC9HYKmkK6rSX1eyPwv3p3cQ4prGa2h-TE +- **备注:** API Key for YouTube Data API v3 + +--- + +## Bilibili UserID +- **用户名:** UserID +- **密码:** 356360432 +- **备注:** B站用户ID DedeUserID + +--- + +## Bilibili SESSDATA +- **用户名:** SESSDATA +- **密码:** 7a9023e2%2C1785783731%2Ce6963%2A21CjCO8WDiZbbX_EbrIi6niuZtTZMzxsl__Yt_Mo0qWXLZY-Pk9pxZ8qp_WxDjD3fdkfoSVlE1TllKNzIxTlVkam5VbVhzano4Q05hZGMtTWdUZ3lMNm1jdWFfdU1CWlY0dDVJU1BWNGI4V0pzN19ualZ2RjVrSHMyRjZlZk5uQWRTVEU5SXFjWXh3IIEC +- **备注:** B站登录凭证 SESSDATA + +--- + +## qBittorrent +- **用户名:** 1803560007 +- **密码:** fengyaxing123 +- **备注:** Downloader on 162.211.228.232:8082 + +--- + +## 滴答清单 API +- **用户名:** Token +- **密码:** dp_9a8e7eccb01b44559e061dc58a669037 +- **备注:** Access Token for Dida365 + +--- + +## Memos Access Token +- **用户名:** fyx +- **密码:** eyJhbGciOiJIUzI1NiIsImtpZCI6InYxIiwidHlwIjoiSldUIn0.eyJuYW1lIjoiZnl4IiwiaXNzIjoibWVtb3MiLCJzdWIiOiIxIiwiYXVkIjpbInVzZXIuYWNjZXNzLXRva2VuIl0sImlhdCI6MTc1NTY0MzI4N30.pIOySEM5VqYSTM-YLwcjiSMS4y10fbGrtTtm3afjjSI +- **备注:** Access Token for Memos (162.211.228.232:5230) + +--- + +## 小红书 +- **用户名:** session +- **密码:** 040069b97e1d876ba1a108d9ce3a4bf940ffca +- **备注:** 小红书 web_session + +--- + +## Notion Integration +- **用户名:** Token +- **密码:** ntn_c43902219395mirQBetIfYoww1qKCAF14GBRUQeDee29o2 +- **备注:** Integration Token for Notion API + +--- + +## NewsAPI +- **用户名:** API Key +- **密码:** 744fb0c696f546cc95545974d18401bb +- **备注:** API Key for newsapi.org + +--- + +## Cubox +- **用户名:** Token +- **密码:** aooyYG5itvB +- **备注:** API Token for Cubox + +--- + +## 163邮箱 SMTP +- **用户名:** work_fyx02@163.com +- **密码:** PU7fV9D2UeVN9duK +- **备注:** SMTP授权码 for work_fyx02@163.com + +--- + +*共 16 条记录 | 存储位置: Vaultwarden (bit.180356.xyz)* diff --git a/vaultwarden_cli.js b/vaultwarden_cli.js new file mode 100644 index 00000000..17cb4d3b --- /dev/null +++ b/vaultwarden_cli.js @@ -0,0 +1,368 @@ +#!/usr/bin/env node +/** + * Vaultwarden CLI Tool (交互版) + * - 交互式输入密码 + * - 支持查看密码 + * - OAuth 自动认证 + */ + +const fs = require('fs'); +const path = require('path'); +const https = require('https'); +const readline = require('readline'); + +// Configuration +const CONFIG_PATH = process.env.VAULTWARDEN_CONFIG_PATH || + path.join(process.env.HOME || process.env.USERPROFILE, '.config', 'vaultwarden', 'config.json'); + +const ENV = { + url: process.env.VAULTWARDEN_URL || 'https://bit.180356.xyz', + clientId: process.env.VAULTWARDEN_CLIENT_ID || '', + clientSecret: process.env.VAULTWARDEN_CLIENT_SECRET || '' +}; + +// Load config file +let config = { url: ENV.url, client_id: '', client_secret: '' }; +if (fs.existsSync(CONFIG_PATH)) { + try { + config = { ...config, ...JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8')) }; + } catch (e) { + console.error('Error reading config:', e.message); + } +} + +const VAULT_URL = ENV.url || config.url; +const CLIENT_ID = ENV.clientId || config.client_id; +const CLIENT_SECRET = ENV.clientSecret || config.client_secret; + +// 交互式输入 +const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout +}); + +function ask(question) { + return new Promise(resolve => { + rl.question(question, answer => resolve(answer)); + }); +} + +class VaultwardenAPI { + constructor() { + this.token = null; + this.tokenExpiry = null; + this.deviceId = 'openclaw-cli-' + require('crypto').randomUUID(); + this.deviceType = '14'; + this.deviceName = 'OpenClaw CLI'; + } + + async getToken() { + if (this.token && this.tokenExpiry && Date.now() < this.tokenExpiry - 300000) { + return this.token; + } + + const params = new URLSearchParams({ + grant_type: 'client_credentials', + scope: 'api', + client_id: CLIENT_ID, + client_secret: CLIENT_SECRET, + device_identifier: this.deviceId, + device_type: this.deviceType, + device_name: this.deviceName + }); + + const response = await this.request( + '/identity/connect/token', + 'POST', + params.toString(), + false, + 'application/x-www-form-urlencoded' + ); + + if (!response.access_token) { + throw new Error('认证失败'); + } + + this.token = response.access_token; + this.tokenExpiry = Date.now() + ((response.expires_in || 7200) - 300) * 1000; + return this.token; + } + + async request(endpoint, method = 'GET', data = null, useAuth = true, contentType = 'application/json') { + const url = new URL(VAULT_URL + endpoint); + + return new Promise((resolve, reject) => { + const options = { + hostname: url.hostname, + port: url.port || 443, + path: url.pathname + url.search, + method: method, + headers: { + 'Content-Type': contentType, + 'Accept': 'application/json' + }, + timeout: 30000 + }; + + if (useAuth && this.token) { + options.headers['Authorization'] = `Bearer ${this.token}`; + } + + const req = https.request(options, (res) => { + let body = ''; + res.on('data', chunk => body += chunk); + res.on('end', () => { + try { + const json = body ? JSON.parse(body) : {}; + if (res.statusCode >= 200 && res.statusCode < 300) { + resolve(json); + } else { + reject(new Error(`HTTP ${res.statusCode}: ${json.message || body}`)); + } + } catch (e) { + reject(e); + } + }); + }); + + req.on('error', reject); + req.on('timeout', () => { req.destroy(); reject(new Error('超时')); }); + + if (data) req.write(data); + req.end(); + }); + } + + async listItems() { + await this.getToken(); + const response = await this.request('/api/ciphers'); + return response.data || []; + } + + async getItem(id) { + await this.getToken(); + return await this.request(`/api/ciphers/${id}`); + } + + async getItemByName(name) { + const items = await this.listItems(); + return items.find(item => item.name?.toLowerCase() === name.toLowerCase()); + } + + async createItem(name, password, username = '', notes = '') { + await this.getToken(); + const itemData = { + name: name, + notes: notes, + type: 1, + favorite: false, + login: { + username: username, + password: password, + totp: null + }, + fields: [], + collectionIds: [] + }; + return await this.request('/api/ciphers', 'POST', JSON.stringify(itemData)); + } + + async deleteItem(id) { + await this.getToken(); + return await this.request(`/api/ciphers/${id}?permanent=true`, 'DELETE'); + } +} + +async function main() { + const args = process.argv.slice(2); + const command = args[0] || 'list'; + + if (!CLIENT_ID || !CLIENT_SECRET) { + console.error('❌ 未配置! 请设置环境变量或配置文件'); + console.log('\n💡 运行: npm run setup'); + process.exit(1); + } + + const api = new VaultwardenAPI(); + + try { + switch (command) { + case 'save': + case 'add': + await handleSave(args.slice(1), api); + break; + case 'list': + await handleList(api); + break; + case 'get': + await handleGet(args.slice(1), api); + break; + case 'search': + await handleSearch(args.slice(1), api); + break; + case 'delete': + await handleDelete(args.slice(1), api); + break; + case 'setup': + await handleSetup(); + break; + case 'help': + default: + showHelp(); + break; + } + } catch (error) { + console.error('\n❌ 错误:', error.message); + process.exit(1); + } finally { + rl.close(); + } +} + +async function handleSave(args, api) { + console.log('\n📝 保存新项目\n'); + + const name = await ask('名称: '); + if (!name) { + console.error('❌ 名称不能为空'); + return; + } + + const username = await ask('用户名 (可选): '); + + const password = await ask('密码: '); + if (!password) { + console.error('❌ 密码不能为空'); + return; + } + + const notes = await ask('备注 (可选): '); + + await api.createItem(name, password, username, notes); + console.log(`\n✅ 已保存: ${name}\n`); +} + +async function handleList(api) { + const items = await api.listItems(); + console.log(`\n📋 项目列表 (${items.length})\n`); + items.forEach(item => { + const name = item.name || 'Unknown'; + const id = item.id?.slice(0, 8) || 'N/A'; + console.log(` • ${name} [${id}]`); + }); + console.log(''); +} + +async function handleGet(args, api) { + const name = args[0]; + if (!name) { + console.error('\n用法: get <名称>\n'); + return; + } + + const item = await api.getItemByName(name); + if (!item) { + console.error(`\n❌ 未找到: ${name}\n`); + return; + } + + console.log(`\n📄 ${item.name}\n`); + console.log(` ID: ${item.id}`); + console.log(` 用户名: ${item.login?.username || '无'}`); + console.log(` 密码: ${item.login?.password || '无'}`); + console.log(` 备注: ${item.notes || '无'}`); + console.log(''); +} + +async function handleSearch(args, api) { + const query = args[0]; + if (!query) { + console.error('\n用法: search <关键词>\n'); + return; + } + + const items = await api.listItems(); + const results = items.filter(item => + item.name?.toLowerCase().includes(query.toLowerCase()) || + item.login?.username?.toLowerCase().includes(query.toLowerCase()) || + item.notes?.toLowerCase().includes(query.toLowerCase()) + ); + + console.log(`\n🔍 搜索 "${query}" (${results.length} 个结果)\n`); + results.forEach(item => { + console.log(` • ${item.name}`); + }); + console.log(''); +} + +async function handleDelete(args, api) { + const name = args[0]; + if (!name) { + console.error('\n用法: delete <名称>\n'); + return; + } + + const item = await api.getItemByName(name); + if (!item) { + console.error(`\n❌ 未找到: ${name}\n`); + return; + } + + const answer = await ask(`🗑️ 删除 "${item.name}"? (y/N): `); + if (answer.toLowerCase() !== 'y' && answer.toLowerCase() !== 'yes') { + console.log('已取消\n'); + return; + } + + await api.deleteItem(item.id); + console.log(`\n✅ 已删除: ${item.name}\n`); +} + +async function handleSetup() { + console.log('\n🔐 Vaultwarden 配置\n'); + + const url = await ask('服务器地址 (默认 https://bit.180356.xyz): '); + const clientId = await ask('Client ID: '); + const clientSecret = await ask('Client Secret: '); + + const configData = { + url: url || 'https://bit.180356.xyz', + client_id: clientId, + client_secret: clientSecret + }; + + fs.mkdirSync(path.dirname(CONFIG_PATH), { recursive: true }); + fs.writeFileSync(CONFIG_PATH, JSON.stringify(configData, null, 2)); + + console.log('\n✅ 配置已保存!\n'); +} + +function showHelp() { + console.log(` +🔐 Vaultwarden CLI (交互版) + +用法: + save 保存新项目 (交互式) + list 列出所有项目 + get <名称> 获取项目详情 + search <关键词> 搜索项目 + delete <名称> 删除项目 + setup 配置 + help 显示帮助 + +示例: + save # 交互式保存 + list # 列出所有 + get GitHub # 获取 GitHub + search GitHub # 搜索 + delete GitHub # 删除 + +注意: 密码会在命令行中显示 +`); +} + +module.exports = {}; + +if (require.main === module) { + main(); +}