oopsdb 1.1.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -63,11 +63,31 @@ async function secureCommand(options) {
63
63
  const fileSizeInBytes = fs.statSync(latestFile).size;
64
64
  const fileSizeMB = (fileSizeInBytes / (1024 * 1024)).toFixed(2);
65
65
  console.log(chalk_1.default.blue(` Found latest snapshot: ${fileName} (${fileSizeMB} MB)`));
66
- // Placeholder URL logic: In the future, this would fetch from the Cloudflare Pages backend
67
- // e.g. const res = await fetch('https://oopsdb.com/api/upload-url', { ... });
68
- const placeholderUploadUrl = 'https://example-bucket.s3.amazonaws.com/placeholder-url-for-mvp';
69
- console.log(chalk_1.default.gray(` Requesting secure upload token...\n`));
70
- await uploadToS3(latestFile, placeholderUploadUrl);
66
+ // In production, hit the live endpoint. If testing locally, hit the wrangler dev server.
67
+ const baseUrl = process.env.TEST_LOCAL_API ? 'http://localhost:8788' : 'https://oopsdb.com';
68
+ console.log(chalk_1.default.gray(` Requesting secure upload token from ${baseUrl}...\n`));
69
+ let actualUploadUrl = '';
70
+ try {
71
+ const res = await fetch(`${baseUrl}/api/upload-url?fileName=${fileName}`, {
72
+ headers: {
73
+ 'Authorization': 'Bearer oops_sec_9f8b2c7d4e5a1b3c8f9d0e2a5b7c8d9e'
74
+ }
75
+ });
76
+ if (!res.ok) {
77
+ const errText = await res.text();
78
+ console.log(chalk_1.default.red(` Failed to get upload token: ${res.status} ${res.statusText}`));
79
+ console.log(chalk_1.default.gray(` Details: ${errText}\n`));
80
+ return;
81
+ }
82
+ const data = await res.json();
83
+ actualUploadUrl = data.uploadUrl;
84
+ }
85
+ catch (err) {
86
+ console.log(chalk_1.default.red(` Network error reaching backend: ${err.message}\n`));
87
+ console.log(chalk_1.default.yellow(` Tip: If testing locally, ensure you ran 'npx wrangler pages dev website' first and set TEST_LOCAL_API=1\n`));
88
+ return;
89
+ }
90
+ await uploadToS3(latestFile, actualUploadUrl);
71
91
  }
72
92
  async function uploadToS3(filePath, uploadUrl) {
73
93
  const size = fs.statSync(filePath).size;
@@ -43,9 +43,15 @@ exports.getEncryptionKey = getEncryptionKey;
43
43
  const fs = __importStar(require("fs"));
44
44
  const path = __importStar(require("path"));
45
45
  const crypto = __importStar(require("crypto"));
46
- const CONFIG_DIR = path.join(process.cwd(), '.oopsdb');
47
- const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
48
- const BACKUPS_DIR = path.join(CONFIG_DIR, 'backups');
46
+ function getConfigDirPath() {
47
+ return path.join(process.cwd(), '.oopsdb');
48
+ }
49
+ function getConfigFilePath() {
50
+ return path.join(getConfigDirPath(), 'config.json');
51
+ }
52
+ function getBackupsDirPath() {
53
+ return path.join(getConfigDirPath(), 'backups');
54
+ }
49
55
  // We encrypt the config file itself using a static machine-local key so that
50
56
  // rogue processes can't easily read the master key in plain text from the file system.
51
57
  // Note: This machineKey is NOT used for the database backups, only the config.json.
@@ -70,16 +76,16 @@ function decryptConfig(text) {
70
76
  }
71
77
  function ensureConfigDir() {
72
78
  try {
73
- if (!fs.existsSync(CONFIG_DIR)) {
74
- fs.mkdirSync(CONFIG_DIR, { recursive: true });
79
+ if (!fs.existsSync(getConfigDirPath())) {
80
+ fs.mkdirSync(getConfigDirPath(), { recursive: true });
75
81
  }
76
- if (!fs.existsSync(BACKUPS_DIR)) {
77
- fs.mkdirSync(BACKUPS_DIR, { recursive: true });
82
+ if (!fs.existsSync(getBackupsDirPath())) {
83
+ fs.mkdirSync(getBackupsDirPath(), { recursive: true });
78
84
  }
79
85
  }
80
86
  catch (err) {
81
87
  if (err.code === 'EACCES') {
82
- throw new Error(`Permission denied creating ${CONFIG_DIR}. Check directory permissions.`);
88
+ throw new Error(`Permission denied creating ${getConfigDirPath()}. Check directory permissions.`);
83
89
  }
84
90
  if (err.code === 'ENOSPC') {
85
91
  throw new Error('Disk full. Free up space and try again.');
@@ -90,14 +96,14 @@ function ensureConfigDir() {
90
96
  function saveConfig(config) {
91
97
  ensureConfigDir();
92
98
  const encrypted = encryptConfig(JSON.stringify(config));
93
- fs.writeFileSync(CONFIG_FILE, encrypted, 'utf8');
99
+ fs.writeFileSync(getConfigFilePath(), encrypted, 'utf8');
94
100
  }
95
101
  function loadConfig() {
96
- if (!fs.existsSync(CONFIG_FILE)) {
102
+ if (!fs.existsSync(getConfigFilePath())) {
97
103
  return null;
98
104
  }
99
105
  try {
100
- const encrypted = fs.readFileSync(CONFIG_FILE, 'utf8');
106
+ const encrypted = fs.readFileSync(getConfigFilePath(), 'utf8');
101
107
  const decrypted = decryptConfig(encrypted);
102
108
  return JSON.parse(decrypted);
103
109
  }
@@ -107,10 +113,10 @@ function loadConfig() {
107
113
  }
108
114
  function getBackupsDir() {
109
115
  ensureConfigDir();
110
- return BACKUPS_DIR;
116
+ return getBackupsDirPath();
111
117
  }
112
118
  function getConfigDir() {
113
- return CONFIG_DIR;
119
+ return getConfigDirPath();
114
120
  }
115
121
  function generateMasterKey() {
116
122
  return crypto.randomBytes(32).toString('hex');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oopsdb",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "description": "Don't let AI nuke your database. Auto-backup and 1-click restore for developers using Claude Code, Cursor, Windsurf, and other AI coding agents.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -54,6 +54,7 @@
54
54
  "@cloudflare/workers-types": "^4.20260310.1",
55
55
  "@types/inquirer": "^8.2.10",
56
56
  "@types/node": "^22.0.0",
57
+ "dotenv": "^17.3.1",
57
58
  "testcontainers": "^11.12.0",
58
59
  "typescript": "^5.7.0",
59
60
  "vitest": "^4.0.18"