main beautify
All checks were successful
Build, Push, Publish / Build & Release (push) Successful in 10m4s

This commit is contained in:
2025-12-13 02:10:50 -03:00
parent b786e43046
commit 0e827a1f4c
7 changed files with 767 additions and 0 deletions

View File

@@ -0,0 +1,383 @@
name: Build, Push, Publish
on:
push:
branches:
- main
workflow_dispatch:
schedule:
- cron: '28 5 * * *'
# workflow_run support in Gitea can be tricky, keeping it but might need adjustment
workflow_run:
workflows: ["Sync Repo"]
types:
- completed
jobs:
release:
name: Build & Release
runs-on: ubuntu-latest
container:
image: catthehacker/ubuntu:act-latest
permissions:
contents: write
packages: write
steps:
- name: 📥 Checkout code with full history and tags
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Check for Dockerargs and Upstream Updates
id: check_upstream
run: |
if [ -f Dockerargs ]; then
echo "Dockerargs found. Checking upstream..."
echo "Dockerargs found. Checking upstream..."
# Parse repo info using awk to avoid git config restrictions on underscores in keys elsewhere in the file
REPO_URL=$(awk -F '=' '/\[repo\]/{flag=1; next} /\[/{flag=0} flag && /^url=/{print $2}' Dockerargs | tr -d ' \r\n')
REPO_BRANCH=$(awk -F '=' '/\[repo\]/{flag=1; next} /\[/{flag=0} flag && /^branch=/{print $2}' Dockerargs | tr -d ' \r\n')
if [ -z "$REPO_BRANCH" ]; then REPO_BRANCH="main"; fi
# Fetch upstream SHA
if [ -n "$REPO_URL" ]; then
UPSTREAM_SHA=$(git ls-remote "$REPO_URL" "$REPO_BRANCH" | awk '{ print $1 }' | head -c 7)
echo "Upstream SHA: $UPSTREAM_SHA"
if [ -f manifest.json ]; then
LOCAL_SHA=$(jq -r '.upstream_sha // empty' manifest.json)
else
LOCAL_SHA=""
fi
if [ "$LOCAL_SHA" != "$UPSTREAM_SHA" ]; then
echo "Upstream changed ($LOCAL_SHA -> $UPSTREAM_SHA)."
echo "upstream_needs_update=true" >> "$GITHUB_OUTPUT"
echo "upstream_sha=$UPSTREAM_SHA" >> "$GITHUB_OUTPUT"
echo "repo_url=$REPO_URL" >> "$GITHUB_OUTPUT"
echo "repo_branch=$REPO_BRANCH" >> "$GITHUB_OUTPUT"
else
echo "Upstream up to date."
echo "upstream_needs_update=false" >> "$GITHUB_OUTPUT"
fi
# Parse Build Args
echo "Parsing [args] from Dockerargs..."
ARGS_CONTENT=$(sed -n '/^\[args\]/,/^\[/p' Dockerargs | grep -v '^\[' | grep '=' || true)
if [ -n "$ARGS_CONTENT" ]; then
echo "Found args:"
echo "$ARGS_CONTENT"
echo "build_args<<EOF" >> "$GITHUB_OUTPUT"
echo "$ARGS_CONTENT" >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT"
else
echo "No args found."
echo "build_args=" >> "$GITHUB_OUTPUT"
fi
else
echo "Repo URL not found in Dockerargs."
echo "upstream_needs_update=false" >> "$GITHUB_OUTPUT"
echo "build_args=" >> "$GITHUB_OUTPUT"
fi
else
echo "No Dockerargs found."
echo "upstream_needs_update=false" >> "$GITHUB_OUTPUT"
echo "build_args=" >> "$GITHUB_OUTPUT"
fi
- name: Check if any tags exist
id: check_tags_exist
run: |
git fetch --tags
TAG_COUNT=$(git tag | wc -l)
if [ "$TAG_COUNT" -eq 0 ]; then
echo "has_tags=false" >> "$GITHUB_OUTPUT"
echo "latest_tag=v0.0.0" >> "$GITHUB_OUTPUT"
else
echo "has_tags=true" >> "$GITHUB_OUTPUT"
LATEST_TAG=$(git describe --tags --abbrev=0)
echo "latest_tag=$LATEST_TAG" >> "$GITHUB_OUTPUT"
fi
- name: Check if meaningful commits exist since latest tag
id: check_commits
run: |
UPSTREAM_UPDATE="${{ steps.check_upstream.outputs.upstream_needs_update }}"
if [ "$UPSTREAM_UPDATE" == "true" ]; then
echo "commit_count=1" >> "$GITHUB_OUTPUT"
echo "changed_files=Upstream Update to ${{ steps.check_upstream.outputs.upstream_sha }}" >> "$GITHUB_OUTPUT"
elif [ "${{ steps.check_tags_exist.outputs.has_tags }}" = "false" ]; then
# No tags exist, so we should create first release
echo "commit_count=1" >> "$GITHUB_OUTPUT"
CHANGED_FILES=$(git ls-files | grep -v '^manifest.json$' || true)
if [ -n "$CHANGED_FILES" ]; then
echo "changed_files<<EOF" >> "$GITHUB_OUTPUT"
printf '%s\n' "$CHANGED_FILES" >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT"
else
echo "changed_files=Initial release" >> "$GITHUB_OUTPUT"
fi
else
LATEST_TAG="${{ steps.check_tags_exist.outputs.latest_tag }}"
CHANGED_FILES="$(git diff --name-only "${LATEST_TAG}..HEAD" | grep -v '^manifest.json$' || true)"
if [ -n "$CHANGED_FILES" ]; then
echo "commit_count=1" >> "$GITHUB_OUTPUT"
echo "changed_files<<EOF" >> "$GITHUB_OUTPUT"
printf '%s\n' "$CHANGED_FILES" >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT"
else
echo "commit_count=0" >> "$GITHUB_OUTPUT"
fi
fi
- name: Get latest release tag (from Gitea API)
id: get_latest_release
run: |
# Using Gitea API
LATEST_RELEASE_TAG=$(curl -sL -H "Accept: application/json" \
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
"${{ gitea.api_url }}/repos/${{ gitea.repository }}/releases/latest" | jq -r .tag_name)
if [ -z "$LATEST_RELEASE_TAG" ] || [ "$LATEST_RELEASE_TAG" = "null" ]; then
LATEST_RELEASE_TAG="v1.0.0"
fi
echo "latest_release_tag=$LATEST_RELEASE_TAG" >> "$GITHUB_OUTPUT"
echo "latest_release_version=${LATEST_RELEASE_TAG#v}" >> "$GITHUB_OUTPUT"
# -------------------------------
# Sync manifest.json to last release version if behind (only when no meaningful commits)
# -------------------------------
- name: 🛠 Ensure manifest.json matches latest release version
if: steps.check_commits.outputs.commit_count == '0'
run: |
if [ -f manifest.json ]; then
MANIFEST_VERSION=$(jq -r '.version // empty' manifest.json)
else
MANIFEST_VERSION=""
fi
LATEST_RELEASE_VERSION="${{ steps.get_latest_release.outputs.latest_release_version }}"
PYTHON_CODE="from packaging import version; \
print(version.parse('$LATEST_RELEASE_VERSION') > version.parse('$MANIFEST_VERSION') if '$MANIFEST_VERSION' else True)"
# Python3 is available in catthehacker/ubuntu:act-latest
NEED_UPDATE=$(python3 -c "$PYTHON_CODE")
if [ "$NEED_UPDATE" = "True" ]; then
echo "Updating manifest.json to version $LATEST_RELEASE_VERSION (sync with release)"
jq --arg v "$LATEST_RELEASE_VERSION" '.version = $v' manifest.json > tmp.json && mv tmp.json manifest.json
git config user.name "Gitea Actions"
git config user.email "actions@git.icc.gg"
git add manifest.json
git commit -m "Sync manifest.json to release $LATEST_RELEASE_VERSION [🔄]" || echo "Nothing to commit"
git push origin main || true
else
echo "Manifest.json is already up-to-date with the latest release."
fi
# -------------------------------
# Continue normal workflow if commits exist
# -------------------------------
- name: 📃 Get list of changed files (Markdown bullet list)
if: steps.check_commits.outputs.commit_count != '0'
id: changed_files
run: |
BULLET_LIST="$(printf '%s\n' "${{ steps.check_commits.outputs.changed_files }}" | sed 's/^/- /')"
echo "CHANGED<<EOF" >> "$GITHUB_OUTPUT"
printf '%s\n' "$BULLET_LIST" >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT"
COUNT="$(printf '%s\n' "${{ steps.check_commits.outputs.changed_files }}" | wc -l)"
echo "COUNT=$COUNT" >> "$GITHUB_OUTPUT"
- name: Get manifest version
if: steps.check_commits.outputs.commit_count != '0'
id: get_manifest_version
run: |
if [ -f manifest.json ]; then
MANIFEST_VERSION=$(jq -r '.version // empty' manifest.json)
if [ -z "$MANIFEST_VERSION" ] || [ "$MANIFEST_VERSION" = "null" ]; then
MANIFEST_VERSION="1.0.0"
fi
else
MANIFEST_VERSION="1.0.0"
fi
echo "manifest_version=$MANIFEST_VERSION" >> "$GITHUB_OUTPUT"
- name: Pick base version
if: steps.check_commits.outputs.commit_count != '0'
id: pick_base_version
run: |
LATEST_RELEASE="${{ steps.get_latest_release.outputs.latest_release_version }}"
MANIFEST="${{ steps.get_manifest_version.outputs.manifest_version }}"
BASE_VERSION=$(python3 -c "from packaging import version; \
print(str(max(version.parse('$LATEST_RELEASE'), version.parse('$MANIFEST'))))")
echo "base_version=$BASE_VERSION" >> "$GITHUB_OUTPUT"
- name: 🔢 Determine version
if: steps.check_commits.outputs.commit_count != '0'
id: version
run: |
BASE_VERSION="${{ steps.pick_base_version.outputs.base_version }}"
MAJOR=$(echo "$BASE_VERSION" | cut -d. -f1)
MINOR=$(echo "$BASE_VERSION" | cut -d. -f2)
PATCH=$(echo "$BASE_VERSION" | cut -d. -f3)
COUNT="${{ steps.changed_files.outputs.COUNT }}"
if [ "$COUNT" -ge 5 ]; then
MAJOR=$((MAJOR + 1))
MINOR=0
PATCH=0
elif [ "$COUNT" -ge 3 ]; then
MINOR=$((MINOR + 1))
PATCH=0
else
PATCH=$((PATCH + 1))
fi
NEW_VERSION="${MAJOR}.${MINOR}.${PATCH}"
REPO_NAME="$(basename "$GITHUB_REPOSITORY")"
ZIP_NAME="${REPO_NAME}-${NEW_VERSION}.zip"
echo "VERSION=$NEW_VERSION" >> "$GITHUB_OUTPUT"
echo "ZIP_NAME=$ZIP_NAME" >> "$GITHUB_OUTPUT"
echo "REPO_NAME=$REPO_NAME" >> "$GITHUB_OUTPUT"
- name: 🛠 Update or create manifest.json
if: steps.check_commits.outputs.commit_count != '0'
run: |
VERSION="${{ steps.version.outputs.VERSION }}"
AUTHOR="Ivan Carlos"
VERSION_FILE="manifest.json"
UPSTREAM_SHA="${{ steps.check_upstream.outputs.upstream_sha }}"
if [ -f "$VERSION_FILE" ]; then
jq --arg v "$VERSION" \
--arg a "$AUTHOR" \
--arg u "$UPSTREAM_SHA" \
'.version = $v | .author = $a | if $u != "" and $u != null then .upstream_sha = $u else . end' \
"$VERSION_FILE" > tmp.json && mv tmp.json "$VERSION_FILE"
else
echo "{ \"version\": \"$VERSION\", \"author\": \"$AUTHOR\", \"upstream_sha\": \"$UPSTREAM_SHA\" }" > "$VERSION_FILE"
fi
- name: 💾 Commit and push updated manifest.json
if: steps.check_commits.outputs.commit_count != '0'
run: |
git config user.name "Gitea Actions"
git config user.email "actions@git.icc.gg"
git add manifest.json
git commit -m "Update manifest version to ${{ steps.version.outputs.VERSION }} [▶️]" || echo "Nothing to commit"
git push origin main
- name: 📦 Create ZIP package (excluding certain files)
if: steps.check_commits.outputs.commit_count != '0'
run: |
ZIP_NAME="${{ steps.version.outputs.ZIP_NAME }}"
zip -r "$ZIP_NAME" . -x ".git/*" ".github/*" "docker/*" ".dockerignore" "CNAME" "Dockerfile" "README.md" "LICENSE" ".gitea/*"
- name: 🚀 Create Gitea Release
if: steps.check_commits.outputs.commit_count != '0'
id: create_release
env:
CHANGELOG_LIST: ${{ steps.changed_files.outputs.CHANGED }}
run: |
TAG_NAME="v${{ steps.version.outputs.VERSION }}"
RELEASE_NAME="${{ steps.version.outputs.REPO_NAME }} v${{ steps.version.outputs.VERSION }}"
# Construct Markdown body safely using env var
# We use printf to avoid interpreting backslashes in the file list
BODY=$(printf "### Changelog\nFiles changed in this release:\n%s" "$CHANGELOG_LIST")
# Create JSON payload using jq
jq -n \
--arg tag_name "$TAG_NAME" \
--arg name "$RELEASE_NAME" \
--arg body "$BODY" \
'{tag_name: $tag_name, name: $name, body: $body, draft: false, prerelease: false}' > release_payload.json
echo "DEBUG: Generated Payload:"
cat release_payload.json
# Create Release
curl -s -X POST "${{ gitea.api_url }}/repos/${{ gitea.repository }}/releases" \
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
-H "Content-Type: application/json" \
-d @release_payload.json > api_response.json
echo "DEBUG: API Response:"
cat api_response.json || true
RELEASE_ID=$(jq -r .id api_response.json)
echo "RELEASE_ID=$RELEASE_ID" >> "$GITHUB_OUTPUT"
if [ "$RELEASE_ID" == "null" ] || [ -z "$RELEASE_ID" ]; then
echo "Failed to create release. Response content:"
cat api_response.json
exit 1
fi
- name: 📤 Upload Release Asset
if: steps.check_commits.outputs.commit_count != '0'
run: |
RELEASE_ID="${{ steps.create_release.outputs.RELEASE_ID }}"
ZIP_NAME="${{ steps.version.outputs.ZIP_NAME }}"
FILE_PATH="./$ZIP_NAME"
curl -s -X POST "${{ gitea.api_url }}/repos/${{ gitea.repository }}/releases/$RELEASE_ID/assets" \
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
-H "Content-Type: application/zip" \
--data-binary @"$FILE_PATH" \
-o /dev/null
# ----- Docker steps -----
- name: Clone Upstream Code (if needed)
if: steps.check_commits.outputs.commit_count != '0' && (steps.check_upstream.outputs.upstream_needs_update == 'true' || steps.check_upstream.outputs.repo_url != '')
run: |
rm -rf upstream_src
git clone --depth 1 --branch ${{ steps.check_upstream.outputs.repo_branch }} ${{ steps.check_upstream.outputs.repo_url }} upstream_src
- name: 🔍 Check if Dockerfile exists
if: steps.check_commits.outputs.commit_count != '0' || steps.check_upstream.outputs.upstream_needs_update == 'true'
id: dockerfile_check
run: |
if [ -n "${{ steps.check_upstream.outputs.repo_url }}" ]; then
if [ -f upstream_src/Dockerfile ]; then
echo "exists=true" >> "$GITHUB_OUTPUT"
else
# Fallback or error? User said "ignore", but we need a dockerfile to build.
# Assuming if upstream_src is present, we trust it, or fail at build time.
# Let's say exists=true and let build fail if missing, per user hint.
echo "exists=true" >> "$GITHUB_OUTPUT"
fi
elif [ -f Dockerfile ]; then
echo "exists=true" >> "$GITHUB_OUTPUT"
else
echo "exists=false" >> "$GITHUB_OUTPUT"
fi
- name: 🔐 Login to Gitea Container Registry
if: steps.check_commits.outputs.commit_count != '0' && steps.dockerfile_check.outputs.exists == 'true'
uses: docker/login-action@v3
with:
registry: git.icc.gg
username: ${{ gitea.actor }}
password: ${{ secrets.CR_PAT }}
- name: 🛠 Set up QEMU
if: steps.check_commits.outputs.commit_count != '0' && steps.dockerfile_check.outputs.exists == 'true'
uses: docker/setup-qemu-action@v3
- name: 🛠 Set up Docker Buildx
if: steps.check_commits.outputs.commit_count != '0' && steps.dockerfile_check.outputs.exists == 'true'
uses: docker/setup-buildx-action@v3
- name: 🐳 Build and Push Docker image
if: steps.check_commits.outputs.commit_count != '0' && steps.dockerfile_check.outputs.exists == 'true'
uses: docker/build-push-action@v5
id: docker_build
with:
context: ${{ steps.check_upstream.outputs.repo_url != '' && './upstream_src' || '.' }}
platforms: linux/amd64,linux/arm64
file: ${{ steps.check_upstream.outputs.repo_url != '' && './upstream_src/Dockerfile' || './Dockerfile' }}
push: true
build-args: |
${{ steps.check_upstream.outputs.build_args }}
tags: |
git.icc.gg/${{ gitea.repository }}:latest
git.icc.gg/${{ gitea.repository }}:${{ steps.version.outputs.VERSION }}

3
Dockerfile Normal file
View File

@@ -0,0 +1,3 @@
FROM nginx:alpine
COPY public/ /usr/share/nginx/html

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 Ivan Carlos
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

4
manifest.json Normal file
View File

@@ -0,0 +1,4 @@
{
"version": "1.0.0",
"author": "Ivan Carlos"
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

354
public/index.html Normal file
View File

@@ -0,0 +1,354 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AWS SES SMTP Password Generator</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
:root {
--primary-color: #ec7211;
/* AWS Orange-ish */
--primary-hover: #c85e0b;
--bg-gradient-start: #f3f4f6;
--bg-gradient-end: #e5e7eb;
--card-bg: #ffffff;
--text-main: #1f2937;
--text-muted: #6b7280;
--border-color: #d1d5db;
--focus-ring: rgba(236, 114, 17, 0.4);
--success-color: #059669;
--success-hover: #047857;
}
body {
font-family: 'Inter', system-ui, -apple-system, sans-serif;
margin: 0;
display: flex;
flex-direction: column;
min-height: 100vh;
background: linear-gradient(135deg, var(--bg-gradient-start) 0%, var(--bg-gradient-end) 100%);
color: var(--text-main);
padding: 20px;
box-sizing: border-box;
}
.container {
width: 100%;
max-width: 550px;
margin: auto;
/* Center vertically and horizontally */
background-color: var(--card-bg);
padding: 40px;
border-radius: 16px;
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
}
h1 {
margin: 0;
font-size: 1.5rem;
font-weight: 700;
color: #111827;
text-align: center;
}
.subtitle {
margin-top: 10px;
margin-bottom: 30px;
color: var(--text-muted);
font-size: 0.95rem;
line-height: 1.5;
text-align: center;
}
label {
display: block;
margin-bottom: 6px;
font-weight: 500;
font-size: 0.9rem;
color: #374151;
}
.form-group {
margin-bottom: 20px;
}
input[type="text"],
select {
width: 100%;
padding: 12px 14px;
border: 1px solid var(--border-color);
border-radius: 8px;
font-size: 1rem;
color: #111827;
background-color: #fff;
transition: border-color 0.2s, box-shadow 0.2s;
box-sizing: border-box;
appearance: none;
}
/* Custom arrow for select */
.select-wrapper {
position: relative;
}
.select-wrapper::after {
content: "▼";
font-size: 0.8rem;
color: var(--text-muted);
position: absolute;
right: 14px;
top: 50%;
transform: translateY(-50%);
pointer-events: none;
}
input[type="text"]:focus,
select:focus {
outline: none;
border-color: var(--primary-color);
box-shadow: 0 0 0 3px var(--focus-ring);
}
/* Result Area */
.result {
margin-top: 25px;
padding: 20px;
background-color: #f9fafb;
border: 1px solid #e5e7eb;
border-radius: 8px;
animation: fadeIn 0.3s ease-out;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(-5px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.result strong {
display: block;
margin-bottom: 8px;
font-size: 0.85rem;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--text-muted);
}
code#smtpPassword {
display: block;
background-color: #fff;
border: 1px solid #e5e7eb;
padding: 12px;
border-radius: 6px;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
font-size: 0.9rem;
word-break: break-all;
color: #111827;
margin-bottom: 15px;
}
button.copy-button {
display: inline-flex;
align-items: center;
justify-content: center;
width: 100%;
padding: 10px 20px;
background-color: var(--success-color);
color: white;
border: none;
border-radius: 8px;
font-weight: 500;
font-size: 0.95rem;
cursor: pointer;
transition: background-color 0.2s, transform 0.1s;
}
button.copy-button:hover {
background-color: var(--success-hover);
}
button.copy-button:active {
transform: scale(0.98);
}
/* Form styling fix for the select wrapper */
.select-wrapper select {
padding-right: 35px;
/* space for arrow */
}
footer {
text-align: center;
margin-top: 40px;
padding-top: 20px;
color: var(--text-muted);
font-size: 0.85rem;
}
footer a {
color: var(--text-muted);
text-decoration: none;
transition: color 0.2s;
}
footer a:hover {
color: var(--primary-color);
text-decoration: underline;
}
@media (max-width: 600px) {
.container {
padding: 30px 20px;
}
}
</style>
</head>
<body>
<div class="container">
<h1>AWS SES SMTP Generator</h1>
<div class="subtitle">
Securely convert your AWS Secret Access Key into an SES SMTP password locally in your browser.
</div>
<form id="smtpForm">
<div class="form-group">
<label for="region">SMTP Region</label>
<div class="select-wrapper">
<select id="region" name="region" required>
<option value="">Select a region</option>
<option value="us-east-2">us-east-2: US East (Ohio)</option>
<option value="us-east-1">us-east-1: US East (N. Virginia)</option>
<option value="us-west-2">us-west-2: US West (Oregon)</option>
<option value="ap-south-1">ap-south-1: Asia Pacific (Mumbai)</option>
<option value="ap-northeast-2">ap-northeast-2: Asia Pacific (Seoul)</option>
<option value="ap-southeast-1">ap-southeast-1: Asia Pacific (Singapore)</option>
<option value="ap-southeast-2">ap-southeast-2: Asia Pacific (Sydney)</option>
<option value="ap-northeast-1">ap-northeast-1: Asia Pacific (Tokyo)</option>
<option value="ca-central-1">ca-central-1: Canada (Central)</option>
<option value="eu-central-1">eu-central-1: Europe (Frankfurt)</option>
<option value="eu-west-1">eu-west-1: Europe (Ireland)</option>
<option value="eu-west-2">eu-west-2: Europe (London)</option>
<option value="eu-south-1">eu-south-1: Europe (Milan)</option>
<option value="eu-north-1">eu-north-1: Europe (Stockholm)</option>
<option value="sa-east-1">sa-east-1: South America (Sao Paulo)</option>
<option value="us-gov-west-1">us-gov-west-1: AWS GovCloud (US)</option>
<option value="us-gov-east-1">us-gov-east-1: AWS GovCloud (US)</option>
</select>
</div>
</div>
<div class="form-group">
<label for="secret">Secret Access Key</label>
<input type="text" id="secret" name="secret" placeholder="e.g. wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
required autocomplete="off">
</div>
</form>
<!-- Hidden initially via script logic in original code, but CSS handles display via JS too -->
<div id="result" class="result" style="display: none;">
<strong>Generated SMTP Password</strong>
<code id="smtpPassword"></code>
<button id="copyButton" class="copy-button">Copy to Clipboard</button>
</div>
<footer>
<a href="https://icc.gg/privacidade" target="_blank">Privacy Policy</a><a href="https://icc.gg/termos"
target="_blank">Terms and Conditions</a>
</footer>
</div>
<!-- Original logic script preserved -->
<script>
// Function to convert a string to a byte array (Uint8Array)
function stringToBytes(str) {
return new TextEncoder().encode(str);
}
// Function to calculate the HMAC-SHA256 signature
async function sign(key, msg) {
const keyBytes = typeof key === 'string' ? stringToBytes(key) : key;
const msgBytes = stringToBytes(msg);
const importedKey = await crypto.subtle.importKey(
"raw",
keyBytes,
{ name: "HMAC", hash: "SHA-256" },
false,
["sign"]
);
const signature = await crypto.subtle.sign("HMAC", importedKey, msgBytes);
return new Uint8Array(signature);
}
// Function to calculate the SMTP password
async function calculateKey(secretAccessKey, region) {
const date = "11111111";
const service = "ses";
const terminal = "aws4_request";
const message = "SendRawEmail";
const version = 0x04;
let signature = await sign("AWS4" + secretAccessKey, date);
signature = await sign(signature, region);
signature = await sign(signature, service);
signature = await sign(signature, terminal);
signature = await sign(signature, message);
// Add the version byte (0x04) to the beginning of the signature
const signatureAndVersion = new Uint8Array([version, ...signature]);
// Encode in Base64
const smtpPassword = btoa(String.fromCharCode(...signatureAndVersion));
return smtpPassword;
}
// Function to update the SMTP password automatically
async function updateSMTPPassword() {
const secret = document.getElementById("secret").value;
const region = document.getElementById("region").value;
if (secret && region) {
try {
const smtpPassword = await calculateKey(secret, region);
document.getElementById("smtpPassword").textContent = smtpPassword;
document.getElementById("result").style.display = "block";
} catch (error) {
console.error("Error generating SMTP password:", error);
}
} else {
document.getElementById("result").style.display = "none";
}
}
// Function to copy the SMTP password to the clipboard
function copyToClipboard() {
const smtpPassword = document.getElementById("smtpPassword").textContent;
navigator.clipboard.writeText(smtpPassword).then(() => {
alert("SMTP password copied to clipboard!");
}).catch((err) => {
console.error("Failed to copy:", err);
});
}
// Update the SMTP password as the user types or selects a region
document.getElementById("secret").addEventListener("input", updateSMTPPassword);
document.getElementById("region").addEventListener("change", updateSMTPPassword);
// Add click event to the copy button
document.getElementById("copyButton").addEventListener("click", copyToClipboard);
</script>
</body>
</html>

2
public/robots.txt Normal file
View File

@@ -0,0 +1,2 @@
User-agent: *
Allow: /