@xuda.io/drive_module 1.1.1488 → 1.1.1490
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.
- package/index.mjs +71 -105
- package/index.mjs.premerge.bak +3514 -0
- package/index.mjs.r2bak +3544 -0
- package/package.json +1 -1
package/index.mjs
CHANGED
|
@@ -19,11 +19,7 @@ import { Upload } from '@aws-sdk/lib-storage';
|
|
|
19
19
|
|
|
20
20
|
console.log('Drive Module loaded...');
|
|
21
21
|
|
|
22
|
-
global._conf = (
|
|
23
|
-
await import(path.join(process.env.XUDA_HOME, process.env.XUDA_CONFIG), {
|
|
24
|
-
with: { type: 'json' },
|
|
25
|
-
})
|
|
26
|
-
).default;
|
|
22
|
+
global._conf = (await import(path.join(process.env.XUDA_HOME, "common", "load_conf.mjs"))).loadConf();
|
|
27
23
|
|
|
28
24
|
const _common = await import(path.join(process.env.XUDA_HOME, 'common', 'xuda_node_common.mjs'));
|
|
29
25
|
const _utils = await import(path.join(process.env.XUDA_HOME, 'common', 'xuda-cpi-utils.mjs'));
|
|
@@ -1803,97 +1799,76 @@ export const get_drive_size = async (req) => {
|
|
|
1803
1799
|
}
|
|
1804
1800
|
};
|
|
1805
1801
|
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1802
|
+
// --- Object storage (S3 / R2) helpers ------------------------------------
|
|
1803
|
+
// Credentials: prefer R2 (post-migration), fall back to DO Spaces so the same
|
|
1804
|
+
// code runs before and after the cutover.
|
|
1805
|
+
const _storage_credentials = () => {
|
|
1806
|
+
if (_conf.r2?.access_key_id && _conf.r2?.secret_access_key) {
|
|
1807
|
+
return { accessKeyId: _conf.r2.access_key_id, secretAccessKey: _conf.r2.secret_access_key };
|
|
1808
|
+
}
|
|
1809
|
+
return {
|
|
1810
|
+
accessKeyId: _conf.digitalocean?.spaces_access_key,
|
|
1811
|
+
secretAccessKey: _conf.digitalocean?.spaces_secret_key,
|
|
1812
|
+
};
|
|
1813
|
+
};
|
|
1816
1814
|
|
|
1817
|
-
|
|
1815
|
+
// R2 addresses buckets path-style against the account endpoint; DO Spaces uses
|
|
1816
|
+
// virtual-hosted style.
|
|
1817
|
+
const _is_r2 = () => !!(_conf.r2?.access_key_id);
|
|
1818
1818
|
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1819
|
+
// Public URL for a stored object, ALWAYS rebuilt from the Key. Post-migration
|
|
1820
|
+
// every bucket is served from one custom domain (r2.public_base); pre-migration
|
|
1821
|
+
// we reconstruct the virtual-hosted DO Spaces URL. Rebuilding from the Key means
|
|
1822
|
+
// existing docs that still carry old DO Locations resolve to the live backend.
|
|
1823
|
+
const _public_url_for = (region_host, key) => {
|
|
1824
|
+
const k = String(key || '').replace(/^\/+/, '');
|
|
1825
|
+
if (!k) return null;
|
|
1826
|
+
if (_conf.r2?.public_base) return `${String(_conf.r2.public_base).replace(/\/+$/, '')}/${k}`;
|
|
1827
|
+
const conf = _conf.storage_bucket?.[region_host];
|
|
1828
|
+
if (!conf) return null;
|
|
1829
|
+
const host = String(conf.endpoint || '').replace(/^https?:\/\//, '').replace(/\/+$/, '');
|
|
1830
|
+
return host ? `https://${conf.name}.${host}/${k}` : null;
|
|
1831
|
+
};
|
|
1825
1832
|
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1833
|
+
const upload_file_to_spaces = async (tempPath, ref, drive_type, file_name) => {
|
|
1834
|
+
const region_host = process.env.XUDA_HOSTNAME;
|
|
1835
|
+
const conf = _conf.storage_bucket?.[region_host];
|
|
1836
|
+
const s3 = _drive_s3_for(region_host);
|
|
1837
|
+
if (!s3 || !conf) {
|
|
1838
|
+
console.error('upload_file_to_spaces: no storage config for', region_host);
|
|
1839
|
+
return;
|
|
1840
|
+
}
|
|
1841
|
+
const Bucket = conf.name;
|
|
1842
|
+
const Key = path.join(region_host, drive_type, ref, _utils.UUID() + '_' + file_name);
|
|
1832
1843
|
|
|
1833
1844
|
try {
|
|
1834
1845
|
const upload = new Upload({
|
|
1835
|
-
client:
|
|
1836
|
-
params:
|
|
1846
|
+
client: s3,
|
|
1847
|
+
params: { Bucket, Key, Body: fs.createReadStream(tempPath), ContentType: _drive_content_type_for(file_name) },
|
|
1837
1848
|
});
|
|
1838
1849
|
|
|
1839
1850
|
const data = await upload.done();
|
|
1840
|
-
|
|
1851
|
+
// Override the SDK Location with the canonical public URL (custom domain on
|
|
1852
|
+
// R2), and pin Bucket/Key so the stored doc.bucket entry is backend-correct.
|
|
1853
|
+
return { ...data, Location: _public_url_for(region_host, Key), Bucket, Key, key: Key };
|
|
1841
1854
|
} catch (err) {
|
|
1842
1855
|
console.error('Error', err);
|
|
1843
1856
|
}
|
|
1844
1857
|
};
|
|
1845
1858
|
|
|
1859
|
+
// Retired under the single R2 namespace: every region resolves to the same
|
|
1860
|
+
// bucket, so there is nothing to copy. Kept as a no-op so existing callers keep
|
|
1861
|
+
// working — returns a backend-correct location rebuilt from the Key.
|
|
1846
1862
|
export const copy_file_to_another_bucket = async (source_region, target_region, file_name) => {
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
forcePathStyle: false,
|
|
1855
|
-
});
|
|
1856
|
-
|
|
1857
|
-
const targetS3 = new S3Client({
|
|
1858
|
-
endpoint: _conf.storage_bucket[target_region].endpoint,
|
|
1859
|
-
region: _conf.storage_bucket[target_region].region,
|
|
1860
|
-
credentials: {
|
|
1861
|
-
accessKeyId: _conf.digitalocean.spaces_access_key,
|
|
1862
|
-
secretAccessKey: _conf.digitalocean.spaces_secret_key,
|
|
1863
|
+
return {
|
|
1864
|
+
code: 1,
|
|
1865
|
+
data: {
|
|
1866
|
+
Location: _public_url_for(target_region, file_name),
|
|
1867
|
+
key: file_name,
|
|
1868
|
+
Key: file_name,
|
|
1869
|
+
Bucket: _conf.storage_bucket?.[target_region]?.name,
|
|
1863
1870
|
},
|
|
1864
|
-
|
|
1865
|
-
});
|
|
1866
|
-
|
|
1867
|
-
try {
|
|
1868
|
-
const { Body } = await sourceS3.send(
|
|
1869
|
-
new GetObjectCommand({
|
|
1870
|
-
Bucket: source_region,
|
|
1871
|
-
Key: file_name,
|
|
1872
|
-
}),
|
|
1873
|
-
);
|
|
1874
|
-
|
|
1875
|
-
const upload = new Upload({
|
|
1876
|
-
client: targetS3,
|
|
1877
|
-
params: {
|
|
1878
|
-
Bucket: target_region,
|
|
1879
|
-
Key: file_name,
|
|
1880
|
-
Body,
|
|
1881
|
-
ACL: 'public-read',
|
|
1882
|
-
},
|
|
1883
|
-
});
|
|
1884
|
-
|
|
1885
|
-
const ret = await upload.done();
|
|
1886
|
-
|
|
1887
|
-
ret.Location = `${_conf.storage_bucket[target_region].endpoint}/${target_region}/${file_name}`;
|
|
1888
|
-
ret.key = file_name;
|
|
1889
|
-
ret.Key = file_name;
|
|
1890
|
-
ret.Bucket = target_region;
|
|
1891
|
-
|
|
1892
|
-
return { code: 1, data: ret };
|
|
1893
|
-
} catch (err) {
|
|
1894
|
-
console.error('Error copying file:', err);
|
|
1895
|
-
return { code: -1, data: err.message };
|
|
1896
|
-
}
|
|
1871
|
+
};
|
|
1897
1872
|
};
|
|
1898
1873
|
|
|
1899
1874
|
const get_folder_size = async (dir, drive_type, app_id, uid) => {
|
|
@@ -1976,19 +1951,17 @@ const get_file_info = async (file_path) => {
|
|
|
1976
1951
|
};
|
|
1977
1952
|
|
|
1978
1953
|
export const delete_file_from_bucket = async (bucket) => {
|
|
1979
|
-
const s3 = new S3Client({
|
|
1980
|
-
endpoint: _conf.storage_bucket[process.env.XUDA_HOSTNAME].endpoint,
|
|
1981
|
-
region: _conf.storage_bucket[process.env.XUDA_HOSTNAME].region,
|
|
1982
|
-
credentials: {
|
|
1983
|
-
accessKeyId: _conf.digitalocean.spaces_access_key,
|
|
1984
|
-
secretAccessKey: _conf.digitalocean.spaces_secret_key,
|
|
1985
|
-
},
|
|
1986
|
-
forcePathStyle: false,
|
|
1987
|
-
});
|
|
1988
|
-
|
|
1989
1954
|
try {
|
|
1990
|
-
for await (let [
|
|
1991
|
-
|
|
1955
|
+
for await (let [region_host, val] of Object.entries(bucket || {})) {
|
|
1956
|
+
const Key = val?.key || val?.Key;
|
|
1957
|
+
if (!Key) continue;
|
|
1958
|
+
// Resolve the client + bucket NAME from config for the file's region (fall
|
|
1959
|
+
// back to this server's region). Never trust the stored val.Bucket — after
|
|
1960
|
+
// the R2 cutover that name (e.g. xuda-bucket-nyc) no longer exists.
|
|
1961
|
+
const s3 = _drive_s3_for(region_host) || _drive_s3_for(process.env.XUDA_HOSTNAME);
|
|
1962
|
+
const Bucket = _conf.storage_bucket?.[region_host]?.name || _conf.storage_bucket?.[process.env.XUDA_HOSTNAME]?.name;
|
|
1963
|
+
if (!s3 || !Bucket) continue;
|
|
1964
|
+
await s3.send(new DeleteObjectCommand({ Bucket, Key }));
|
|
1992
1965
|
}
|
|
1993
1966
|
} catch (err) {
|
|
1994
1967
|
console.error('Error', err);
|
|
@@ -2920,11 +2893,8 @@ const _drive_s3_for = (region_host) => {
|
|
|
2920
2893
|
_drive_s3_cache[region_host] = new S3Client({
|
|
2921
2894
|
endpoint: conf.endpoint,
|
|
2922
2895
|
region: conf.region,
|
|
2923
|
-
credentials:
|
|
2924
|
-
|
|
2925
|
-
secretAccessKey: _conf.digitalocean.spaces_secret_key,
|
|
2926
|
-
},
|
|
2927
|
-
forcePathStyle: false,
|
|
2896
|
+
credentials: _storage_credentials(),
|
|
2897
|
+
forcePathStyle: _is_r2(),
|
|
2928
2898
|
});
|
|
2929
2899
|
}
|
|
2930
2900
|
return _drive_s3_cache[region_host];
|
|
@@ -3044,10 +3014,6 @@ export const put_site_files = async (req) => {
|
|
|
3044
3014
|
const Bucket = conf.name;
|
|
3045
3015
|
if (!s3 || !Bucket) return { code: -1, data: `no S3 client for region ${region_host}` };
|
|
3046
3016
|
|
|
3047
|
-
const endpoint_host = String(conf.endpoint || '')
|
|
3048
|
-
.replace(/^https?:\/\//, '')
|
|
3049
|
-
.replace(/\/+$/, '');
|
|
3050
|
-
|
|
3051
3017
|
const manifest = {};
|
|
3052
3018
|
let bytes = 0;
|
|
3053
3019
|
for (const f of files) {
|
|
@@ -3062,11 +3028,11 @@ export const put_site_files = async (req) => {
|
|
|
3062
3028
|
const Key = `sites/${site_id}/${version}${norm}`;
|
|
3063
3029
|
const up = new Upload({
|
|
3064
3030
|
client: s3,
|
|
3065
|
-
params: { Bucket, Key, Body: body,
|
|
3031
|
+
params: { Bucket, Key, Body: body, ContentType: content_type },
|
|
3066
3032
|
});
|
|
3067
|
-
|
|
3068
|
-
//
|
|
3069
|
-
const location =
|
|
3033
|
+
await up.done();
|
|
3034
|
+
// Canonical public URL, rebuilt from the Key (custom domain on R2).
|
|
3035
|
+
const location = _public_url_for(region_host, Key);
|
|
3070
3036
|
manifest[norm] = { key: Key, bucket: Bucket, region: region_host, location, content_type, bytes: body.length };
|
|
3071
3037
|
bytes += body.length;
|
|
3072
3038
|
}
|