launchpd 0.1.2 → 0.1.5

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.

Potentially problematic release.


This version of launchpd might be problematic. Click here for more details.

@@ -1,22 +1,15 @@
1
- import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
2
1
  import { readdir, readFile } from 'node:fs/promises';
3
2
  import { join, relative, posix, sep } from 'node:path';
4
3
  import mime from 'mime-types';
5
4
  import { config } from '../config.js';
6
- import { info } from './logger.js';
5
+
6
+ const API_BASE_URL = `https://api.${config.domain}`;
7
7
 
8
8
  /**
9
- * Create S3-compatible client for Cloudflare R2
9
+ * Get API key for requests
10
10
  */
11
- function createR2Client() {
12
- return new S3Client({
13
- region: 'auto',
14
- endpoint: `https://${config.r2.accountId}.r2.cloudflarestorage.com`,
15
- credentials: {
16
- accessKeyId: config.r2.accessKeyId,
17
- secretAccessKey: config.r2.secretAccessKey,
18
- },
19
- });
11
+ function getApiKey() {
12
+ return process.env.STATICLAUNCH_API_KEY || 'public-beta-key';
20
13
  }
21
14
 
22
15
  /**
@@ -29,13 +22,77 @@ function toPosixPath(windowsPath) {
29
22
  }
30
23
 
31
24
  /**
32
- * Upload a folder to R2 under a subdomain prefix with versioning
25
+ * Upload a single file via API proxy
26
+ * @param {Buffer} content - File content
27
+ * @param {string} subdomain - Target subdomain
28
+ * @param {number} version - Version number
29
+ * @param {string} filePath - Relative file path
30
+ * @param {string} contentType - MIME type
31
+ */
32
+ async function uploadFile(content, subdomain, version, filePath, contentType) {
33
+ const response = await fetch(`${API_BASE_URL}/api/upload/file`, {
34
+ method: 'POST',
35
+ headers: {
36
+ 'X-API-Key': getApiKey(),
37
+ 'X-Subdomain': subdomain,
38
+ 'X-Version': String(version),
39
+ 'X-File-Path': filePath,
40
+ 'X-Content-Type': contentType,
41
+ 'Content-Type': 'application/octet-stream',
42
+ },
43
+ body: content,
44
+ });
45
+
46
+ if (!response.ok) {
47
+ const error = await response.json().catch(() => ({ error: 'Upload failed' }));
48
+ throw new Error(error.error || `Upload failed: ${response.status}`);
49
+ }
50
+
51
+ return response.json();
52
+ }
53
+
54
+ /**
55
+ * Mark upload complete and set active version
56
+ * @param {string} subdomain - Target subdomain
57
+ * @param {number} version - Version number
58
+ * @param {number} fileCount - Number of files uploaded
59
+ * @param {number} totalBytes - Total bytes uploaded
60
+ * @param {string} folderName - Original folder name
61
+ * @param {string|null} expiresAt - ISO expiration timestamp
62
+ */
63
+ async function completeUpload(subdomain, version, fileCount, totalBytes, folderName, expiresAt) {
64
+ const response = await fetch(`${API_BASE_URL}/api/upload/complete`, {
65
+ method: 'POST',
66
+ headers: {
67
+ 'X-API-Key': getApiKey(),
68
+ 'Content-Type': 'application/json',
69
+ },
70
+ body: JSON.stringify({
71
+ subdomain,
72
+ version,
73
+ fileCount,
74
+ totalBytes,
75
+ folderName,
76
+ expiresAt,
77
+ }),
78
+ });
79
+
80
+ if (!response.ok) {
81
+ const error = await response.json().catch(() => ({ error: 'Complete upload failed' }));
82
+ throw new Error(error.error || `Complete upload failed: ${response.status}`);
83
+ }
84
+
85
+ return response.json();
86
+ }
87
+
88
+ /**
89
+ * Upload a folder to Launchpd via API proxy
33
90
  * @param {string} localPath - Local folder path
34
91
  * @param {string} subdomain - Subdomain to use as bucket prefix
35
92
  * @param {number} version - Version number for this deployment
93
+ * @param {function} onProgress - Progress callback (uploaded, total, fileName)
36
94
  */
37
- export async function uploadFolder(localPath, subdomain, version = 1) {
38
- const client = createR2Client();
95
+ export async function uploadFolder(localPath, subdomain, version = 1, onProgress = null) {
39
96
  const files = await readdir(localPath, { recursive: true, withFileTypes: true });
40
97
 
41
98
  let uploaded = 0;
@@ -48,27 +105,39 @@ export async function uploadFolder(localPath, subdomain, version = 1) {
48
105
  // Build full local path
49
106
  const fullPath = join(file.parentPath || file.path, file.name);
50
107
 
51
- // Build R2 key: subdomain/v{version}/relative/path/to/file.ext
108
+ // Build relative path for R2 key
52
109
  const relativePath = relative(localPath, fullPath);
53
- const key = `${subdomain}/v${version}/${toPosixPath(relativePath)}`;
110
+ const posixPath = toPosixPath(relativePath);
54
111
 
55
112
  // Detect content type
56
113
  const contentType = mime.lookup(file.name) || 'application/octet-stream';
57
114
 
58
- // Read file and upload
115
+ // Read file and upload via API
59
116
  const body = await readFile(fullPath);
60
117
  totalBytes += body.length;
61
118
 
62
- await client.send(new PutObjectCommand({
63
- Bucket: config.r2.bucketName,
64
- Key: key,
65
- Body: body,
66
- ContentType: contentType,
67
- }));
119
+ await uploadFile(body, subdomain, version, posixPath, contentType);
68
120
 
69
121
  uploaded++;
70
- info(` Uploaded (${uploaded}/${total}): ${toPosixPath(relativePath)}`);
122
+
123
+ // Call progress callback if provided
124
+ if (onProgress) {
125
+ onProgress(uploaded, total, posixPath);
126
+ }
71
127
  }
72
128
 
73
129
  return { uploaded, subdomain, totalBytes };
74
130
  }
131
+
132
+ /**
133
+ * Complete the upload and set active version
134
+ * @param {string} subdomain - Target subdomain
135
+ * @param {number} version - Version number
136
+ * @param {number} fileCount - Number of files
137
+ * @param {number} totalBytes - Total bytes
138
+ * @param {string} folderName - Folder name
139
+ * @param {string|null} expiresAt - Expiration ISO timestamp
140
+ */
141
+ export async function finalizeUpload(subdomain, version, fileCount, totalBytes, folderName, expiresAt = null) {
142
+ return completeUpload(subdomain, version, fileCount, totalBytes, folderName, expiresAt);
143
+ }