feat new s3 public url option

This commit is contained in:
JamesFlare1212
2026-03-15 19:40:59 -04:00
parent cb7f99dc09
commit b18b8a85e0
5 changed files with 127 additions and 7 deletions

View File

@@ -1,5 +1,6 @@
{ {
"lockfileVersion": 1, "lockfileVersion": 1,
"configVersion": 0,
"workspaces": { "workspaces": {
"": { "": {
"name": "dsas-cca-backend-bun", "name": "dsas-cca-backend-bun",
@@ -16,9 +17,10 @@
}, },
"devDependencies": { "devDependencies": {
"@types/bun": "latest", "@types/bun": "latest",
"typescript-language-server": "^5.1.3",
}, },
"peerDependencies": { "peerDependencies": {
"typescript": "^5", "typescript": "^5.9.3",
}, },
}, },
}, },
@@ -247,7 +249,9 @@
"type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="], "type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="],
"typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
"typescript-language-server": ["typescript-language-server@5.1.3", "", { "bin": { "typescript-language-server": "lib/cli.mjs" } }, "sha512-r+pAcYtWdN8tKlYZPwiiHNA2QPjXnI02NrW5Sf2cVM3TRtuQ3V9EKKwOxqwaQ0krsaEXk/CbN90I5erBuf84Vg=="],
"undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],

View File

@@ -4,6 +4,7 @@ PORT=3000
FIXED_STAFF_ACTIVITY_ID=7095 FIXED_STAFF_ACTIVITY_ID=7095
ALLOWED_ORIGINS=* ALLOWED_ORIGINS=*
S3_ENDPOINT= S3_ENDPOINT=
S3_PUBLIC_URL=
S3_BUCKET_NAME= S3_BUCKET_NAME=
S3_ACCESS_KEY_ID= S3_ACCESS_KEY_ID=
S3_SECRET_ACCESS_KEY= S3_SECRET_ACCESS_KEY=

112
extract-login-form.js Normal file
View File

@@ -0,0 +1,112 @@
import { chromium } from 'playwright';
async function extractLoginForm() {
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage();
console.log('Navigating to login page...\n');
await page.goto('https://engage.nkcswx.cn/Login.aspx');
await page.waitForLoadState('networkidle');
// Extract all form fields
const formFields = await page.evaluate(() => {
const form = document.querySelector('form');
if (!form) return { error: 'No form found' };
const fields = [];
// Get all inputs from the form
const inputs = form.querySelectorAll('input, select, textarea');
inputs.forEach((input, index) => {
fields.push({
index,
type: input.tagName,
name: input.name || '(no name)',
id: input.id || '(no id)',
inputType: input.type || 'N/A',
value: input.type === 'password' ? '[HIDDEN]' : (input.value || '(empty)'),
placeholder: input.placeholder || '(none)',
required: input.required ? 'yes' : 'no',
autocomplete: input.autocomplete || '(none)'
});
});
// Get form attributes
const formAttrs = {
action: form.action,
method: form.method,
enctype: form.enctype,
target: form.target
};
return { formAttrs, fields };
});
if (formFields.error) {
console.error(formFields.error);
await browser.close();
return;
}
console.log('='.repeat(70));
console.log('FORM ATTRIBUTES');
console.log('='.repeat(70));
console.log(`Action: ${formFields.formAttrs.action}`);
console.log(`Method: ${formFields.formAttrs.method}`);
console.log(`Enctype: ${formFields.formAttrs.enctype}`);
console.log(`Target: ${formFields.formAttrs.target || '(default)'}`);
console.log('');
console.log('='.repeat(70));
console.log('ALL FORM FIELDS');
console.log('='.repeat(70));
console.log('');
// Group fields by type
const hiddenFields = formFields.fields.filter(f => f.inputType === 'hidden');
const visibleFields = formFields.fields.filter(f => f.inputType !== 'hidden');
// Show hidden fields first (critical for ASP.NET)
if (hiddenFields.length > 0) {
console.log('📦 HIDDEN FIELDS (critical for form submission):');
console.log('-'.repeat(70));
hiddenFields.forEach(field => {
console.log(` Name: ${field.name}`);
console.log(` Value: ${field.value.substring(0, 80)}${field.value.length > 80 ? '...' : ''}`);
console.log(` Length: ${field.value.length} chars`);
console.log('');
});
}
// Show visible fields
if (visibleFields.length > 0) {
console.log('\n📝 VISIBLE FIELDS:');
console.log('-'.repeat(70));
visibleFields.forEach(field => {
console.log(` Name: ${field.name}`);
console.log(` Type: ${field.inputType}`);
console.log(` ID: ${field.id}`);
console.log(` Placeholder: ${field.placeholder}`);
console.log(` Required: ${field.required}`);
console.log(` Autocomplete: ${field.autocomplete}`);
console.log('');
});
}
// Summary of field names
console.log('='.repeat(70));
console.log('FIELD NAME SUMMARY (for authentication payload):');
console.log('='.repeat(70));
formFields.fields.forEach(field => {
const marker = field.inputType === 'hidden' ? '[HIDDEN]' : '[VISIBLE]';
console.log(` ${marker} ${field.name}`);
});
// Take a screenshot for visual reference
await page.screenshot({ path: 'login-page-screenshot.png', fullPage: true });
console.log('\n📸 Screenshot saved to: login-page-screenshot.png');
await browser.close();
}
extractLoginForm().catch(console.error);

View File

@@ -6,10 +6,11 @@
"dev": "bun run --watch index.ts" "dev": "bun run --watch index.ts"
}, },
"devDependencies": { "devDependencies": {
"@types/bun": "latest" "@types/bun": "latest",
"typescript-language-server": "^5.1.3"
}, },
"peerDependencies": { "peerDependencies": {
"typescript": "^5" "typescript": "^5.9.3"
}, },
"dependencies": { "dependencies": {
"axios": "^1.9.0", "axios": "^1.9.0",

View File

@@ -15,6 +15,7 @@ const S3_REGION = process.env.S3_REGION;
const S3_ACCESS_KEY_ID = process.env.S3_ACCESS_KEY_ID; const S3_ACCESS_KEY_ID = process.env.S3_ACCESS_KEY_ID;
const S3_SECRET_ACCESS_KEY = process.env.S3_SECRET_ACCESS_KEY; const S3_SECRET_ACCESS_KEY = process.env.S3_SECRET_ACCESS_KEY;
const BUCKET_NAME = process.env.S3_BUCKET_NAME; const BUCKET_NAME = process.env.S3_BUCKET_NAME;
const S3_PUBLIC_URL = process.env.S3_PUBLIC_URL;
const PUBLIC_URL_FILE_PREFIX = (process.env.S3_PUBLIC_URL_PREFIX || 'files').replace(/\/$/, ''); const PUBLIC_URL_FILE_PREFIX = (process.env.S3_PUBLIC_URL_PREFIX || 'files').replace(/\/$/, '');
// Initialize S3 client // Initialize S3 client
@@ -195,6 +196,7 @@ export async function deleteS3Objects(objectKeysArray: string[]): Promise<boolea
/** /**
* Constructs the public S3 URL for an object key. * Constructs the public S3 URL for an object key.
* Uses S3_PUBLIC_URL if set (reverse proxy scenario), otherwise uses S3_ENDPOINT.
* @param objectKey - The key of the object in S3 * @param objectKey - The key of the object in S3
* @returns The full public URL * @returns The full public URL
*/ */
@@ -202,8 +204,8 @@ export function constructS3Url(objectKey: string): string {
if (!S3_ENDPOINT || !BUCKET_NAME) { if (!S3_ENDPOINT || !BUCKET_NAME) {
return ''; return '';
} }
// Ensure S3_ENDPOINT does not end with a slash // Use S3_PUBLIC_URL if set (reverse proxy), otherwise use S3_ENDPOINT
const s3Base = S3_ENDPOINT.replace(/\/$/, ''); const s3Base = (S3_PUBLIC_URL || S3_ENDPOINT).replace(/\/$/, '');
// Ensure BUCKET_NAME does not start or end with a slash // Ensure BUCKET_NAME does not start or end with a slash
const bucket = BUCKET_NAME.replace(/^\//, '').replace(/\/$/, ''); const bucket = BUCKET_NAME.replace(/^\//, '').replace(/\/$/, '');
// Ensure objectKey does not start with a slash // Ensure objectKey does not start with a slash