mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
ready to test
Testing Signed-off-by: Kenneth Obsequio <k80308392@gmail.com>
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
// services/s3.service.js
|
||||
//
|
||||
// S3-compatible storage service targeting Garage (self-hosted).
|
||||
// Exposes the same interface as chibisafe.service.js:
|
||||
// uploadFile({ buffer, originalname, mimetype, ownerType }) → { url, uuid }
|
||||
// deleteFile(key)
|
||||
//
|
||||
// Required .env vars:
|
||||
// S3_ENDPOINT – http://127.0.0.1:3900
|
||||
// S3_REGION – garage
|
||||
// S3_ACCESS_KEY
|
||||
// S3_SECRET_KEY
|
||||
// S3_BUCKET – philproperties
|
||||
// S3_PUBLIC_URL – https://garage.philproperties.com
|
||||
|
||||
const { S3Client, PutObjectCommand, DeleteObjectCommand, GetObjectCommand, HeadBucketCommand } = require("@aws-sdk/client-s3");
|
||||
const { getSignedUrl } = require("@aws-sdk/s3-request-presigner");
|
||||
const crypto = require("crypto");
|
||||
const path = require("path");
|
||||
|
||||
// ─── Client ───────────────────────────────────────────────────────────────────
|
||||
|
||||
const s3 = new S3Client({
|
||||
endpoint: process.env.S3_ENDPOINT,
|
||||
region: process.env.S3_REGION || "garage",
|
||||
credentials: {
|
||||
accessKeyId: process.env.S3_ACCESS_KEY,
|
||||
secretAccessKey: process.env.S3_SECRET_KEY,
|
||||
},
|
||||
forcePathStyle: true, // required — Garage does not support DNS-style bucket addressing
|
||||
});
|
||||
|
||||
const DEFAULT_BUCKET = process.env.S3_BUCKET || "philproperties";
|
||||
const PUBLIC_URL = (process.env.S3_PUBLIC_URL || "").replace(/\/$/, "");
|
||||
|
||||
// ─── Key prefix map ───────────────────────────────────────────────────────────
|
||||
//
|
||||
// ownerType mirrors what the controller already passes to chibisafe
|
||||
// (image, video, audio, document, avatar, thumbnail).
|
||||
// Maps to folder prefixes inside the bucket.
|
||||
//
|
||||
const PREFIX_MAP = {
|
||||
image: "images",
|
||||
video: "videos",
|
||||
audio: "audios",
|
||||
document: "documents",
|
||||
avatar: "avatars",
|
||||
thumbnail: "thumbnails",
|
||||
};
|
||||
|
||||
function resolvePrefix(ownerType = "image") {
|
||||
return PREFIX_MAP[ownerType] || "others";
|
||||
}
|
||||
|
||||
function resolveExtension(originalname = "") {
|
||||
return path.extname(originalname).replace(".", "").toLowerCase() || "bin";
|
||||
}
|
||||
|
||||
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
||||
|
||||
// Builds the S3 object key: {prefix}/{uuid}.{ext}
|
||||
// e.g. images/3f2a1b4c-uuid.jpg
|
||||
function buildKey(originalname, ownerType) {
|
||||
const prefix = resolvePrefix(ownerType);
|
||||
const ext = resolveExtension(originalname);
|
||||
const uuid = crypto.randomUUID();
|
||||
return `${prefix}/${uuid}.${ext}`;
|
||||
}
|
||||
|
||||
// Builds the public URL for a stored object.
|
||||
// Garage path-style: {S3_PUBLIC_URL}/{bucket}/{key}
|
||||
// e.g. https://garage.philproperties.com/philproperties/images/uuid.jpg
|
||||
function buildPublicUrl(key, bucket = DEFAULT_BUCKET) {
|
||||
return `${PUBLIC_URL}/${bucket}/${key}`;
|
||||
}
|
||||
|
||||
// ─── uploadFile ───────────────────────────────────────────────────────────────
|
||||
//
|
||||
// Matches chibisafe.service.js interface exactly.
|
||||
// input: { buffer, originalname, mimetype, ownerType? }
|
||||
// output: { url, uuid } ← uuid = the S3 key, stored as storage_key in DB
|
||||
//
|
||||
async function uploadFile({ buffer, originalname, mimetype, ownerType = "image" }) {
|
||||
if (!buffer) {
|
||||
throw Object.assign(new Error("File buffer is required for S3 uploads."), { status: 400 });
|
||||
}
|
||||
|
||||
const bucket = DEFAULT_BUCKET;
|
||||
const key = buildKey(originalname, ownerType);
|
||||
|
||||
await s3.send(new PutObjectCommand({
|
||||
Bucket: bucket,
|
||||
Key: key,
|
||||
Body: buffer,
|
||||
ContentType: mimetype,
|
||||
}));
|
||||
|
||||
return {
|
||||
url: buildPublicUrl(key, bucket),
|
||||
uuid: key, // used as storage_key in DB — mirrors chibi_uuid usage
|
||||
};
|
||||
}
|
||||
|
||||
// ─── deleteFile ───────────────────────────────────────────────────────────────
|
||||
//
|
||||
// Matches chibisafe.service.js interface.
|
||||
// input: key — the value stored in storage_key column (e.g. "images/uuid.jpg")
|
||||
//
|
||||
async function deleteFile(key) {
|
||||
if (!key) return;
|
||||
await s3.send(new DeleteObjectCommand({
|
||||
Bucket: DEFAULT_BUCKET,
|
||||
Key: key,
|
||||
}));
|
||||
}
|
||||
|
||||
// ─── getSignedDownloadUrl ─────────────────────────────────────────────────────
|
||||
//
|
||||
// Generates a short-lived pre-signed GET URL.
|
||||
// Useful if you ever need gated access to private assets (is_public = false).
|
||||
//
|
||||
async function getSignedDownloadUrl(key, expiresInSeconds = 3600) {
|
||||
return getSignedUrl(
|
||||
s3,
|
||||
new GetObjectCommand({ Bucket: DEFAULT_BUCKET, Key: key }),
|
||||
{ expiresIn: expiresInSeconds }
|
||||
);
|
||||
}
|
||||
|
||||
// ─── getObjectStream ────────────────────────────────────────────────────────
|
||||
//
|
||||
// Fetches an S3 object and returns its readable stream + metadata, for use
|
||||
// in a server-side download proxy (Content-Disposition: attachment).
|
||||
//
|
||||
// input: key — the storage_key (e.g. "images/uuid.jpg")
|
||||
// output: { stream, contentType, contentLength }
|
||||
//
|
||||
async function getObjectStream(key) {
|
||||
if (!key) {
|
||||
throw Object.assign(new Error("Storage key is required."), { status: 400 });
|
||||
}
|
||||
|
||||
const result = await s3.send(new GetObjectCommand({
|
||||
Bucket: DEFAULT_BUCKET,
|
||||
Key: key,
|
||||
}));
|
||||
|
||||
return {
|
||||
stream: result.Body, // Node.js Readable stream
|
||||
contentType: result.ContentType,
|
||||
contentLength: result.ContentLength,
|
||||
};
|
||||
}
|
||||
|
||||
async function ping() {
|
||||
await s3.send(new HeadBucketCommand({ Bucket: DEFAULT_BUCKET }));
|
||||
}
|
||||
|
||||
module.exports = { uploadFile, deleteFile, getSignedDownloadUrl, getObjectStream, ping };
|
||||
Reference in New Issue
Block a user