231 lines
9.0 KiB
Plaintext
231 lines
9.0 KiB
Plaintext
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<title>Discord Clone - Create Access Token</title>
|
|
<link rel="stylesheet" href="static/css/index.css"/>
|
|
</head>
|
|
<body>
|
|
<div class="signup-container">
|
|
<div class="signup-header">
|
|
<img src="/static/favicon.ico" class="logo"/>
|
|
<h1>Create Access Token</h1>
|
|
<p>Generate a new invite token</p>
|
|
</div>
|
|
|
|
<div class="signup-form">
|
|
<div class="success-message" id="successMessage">
|
|
<strong>Token Created Successfully!</strong>
|
|
<div id="tokenDisplay" style="margin-top: 10px; word-break: break-all; font-family: monospace; background: rgba(0,0,0,0.2); padding: 10px; border-radius: 4px;"></div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="tokenName">Token Name</label>
|
|
<input
|
|
type="text"
|
|
id="tokenName"
|
|
name="tokenName"
|
|
placeholder="e.g., January 2025 Invites"
|
|
required
|
|
/>
|
|
<div class="error-message">Token name is required</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="maxUses">Max Uses</label>
|
|
<input
|
|
type="number"
|
|
id="maxUses"
|
|
name="maxUses"
|
|
placeholder="Maximum number of uses (leave empty for unlimited)"
|
|
min="1"
|
|
/>
|
|
<div class="error-message">Must be a positive number</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="startDate">Start Date</label>
|
|
<input
|
|
type="datetime-local"
|
|
id="startDate"
|
|
name="startDate"
|
|
required
|
|
/>
|
|
<div class="error-message">Start date is required</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="expiryDate">Expiry Date</label>
|
|
<input
|
|
type="datetime-local"
|
|
id="expiryDate"
|
|
name="expiryDate"
|
|
required
|
|
/>
|
|
<div class="error-message">Expiry date must be after start date</div>
|
|
</div>
|
|
|
|
<button type="button" class="submit-button" id="submitButton">
|
|
Generate Token
|
|
</button>
|
|
</div>
|
|
|
|
<div class="signup-footer">
|
|
<a href="/chat">Back to Dashboard</a>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
const form = {
|
|
tokenName: document.getElementById("tokenName"),
|
|
maxUses: document.getElementById("maxUses"),
|
|
startDate: document.getElementById("startDate"),
|
|
expiryDate: document.getElementById("expiryDate"),
|
|
};
|
|
|
|
const submitButton = document.getElementById("submitButton");
|
|
const successMessage = document.getElementById("successMessage");
|
|
const tokenDisplay = document.getElementById("tokenDisplay");
|
|
|
|
// Set default start date to now
|
|
const now = new Date();
|
|
now.setMinutes(now.getMinutes() - now.getTimezoneOffset());
|
|
form.startDate.value = now.toISOString().slice(0, 16);
|
|
|
|
// Set default expiry date to 30 days from now
|
|
const defaultExpiry = new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000);
|
|
form.expiryDate.value = defaultExpiry.toISOString().slice(0, 16);
|
|
|
|
// Validation functions
|
|
function validateTokenName() {
|
|
const value = form.tokenName.value.trim();
|
|
const isValid = value.length >= 1;
|
|
toggleError(form.tokenName, !isValid);
|
|
return isValid;
|
|
}
|
|
|
|
function validateMaxUses() {
|
|
const value = form.maxUses.value;
|
|
// Empty is valid (unlimited), otherwise must be positive number
|
|
const isValid = value === "" || (parseInt(value) > 0);
|
|
toggleError(form.maxUses, !isValid);
|
|
return isValid;
|
|
}
|
|
|
|
function validateStartDate() {
|
|
const value = form.startDate.value;
|
|
const isValid = value !== "";
|
|
toggleError(form.startDate, !isValid);
|
|
return isValid;
|
|
}
|
|
|
|
function validateExpiryDate() {
|
|
const startValue = form.startDate.value;
|
|
const expiryValue = form.expiryDate.value;
|
|
|
|
if (expiryValue === "") {
|
|
toggleError(form.expiryDate, true);
|
|
return false;
|
|
}
|
|
|
|
const startDate = new Date(startValue);
|
|
const expiryDate = new Date(expiryValue);
|
|
const isValid = expiryDate > startDate;
|
|
toggleError(form.expiryDate, !isValid);
|
|
return isValid;
|
|
}
|
|
|
|
function toggleError(input, hasError) {
|
|
const formGroup = input.closest(".form-group");
|
|
if (hasError) {
|
|
formGroup.classList.add("error");
|
|
} else {
|
|
formGroup.classList.remove("error");
|
|
}
|
|
}
|
|
|
|
// Add blur validation
|
|
form.tokenName.addEventListener("blur", validateTokenName);
|
|
form.maxUses.addEventListener("blur", validateMaxUses);
|
|
form.startDate.addEventListener("blur", validateStartDate);
|
|
form.expiryDate.addEventListener("blur", validateExpiryDate);
|
|
|
|
// Form submission
|
|
submitButton.addEventListener("click", async function () {
|
|
// Validate all fields
|
|
const isTokenNameValid = validateTokenName();
|
|
const isMaxUsesValid = validateMaxUses();
|
|
const isStartDateValid = validateStartDate();
|
|
const isExpiryDateValid = validateExpiryDate();
|
|
|
|
if (
|
|
!isTokenNameValid ||
|
|
!isMaxUsesValid ||
|
|
!isStartDateValid ||
|
|
!isExpiryDateValid
|
|
) {
|
|
return;
|
|
}
|
|
|
|
// Disable button and show loading
|
|
submitButton.disabled = true;
|
|
submitButton.innerHTML =
|
|
'<span class="loading-spinner"></span>Generating Token...';
|
|
|
|
// Prepare data
|
|
const formData = {
|
|
name: form.tokenName.value.trim(),
|
|
max_uses: form.maxUses.value ? parseInt(form.maxUses.value) : null,
|
|
start_date: new Date(form.startDate.value).getTime(),
|
|
expiry_date: new Date(form.expiryDate.value).getTime(),
|
|
};
|
|
|
|
try {
|
|
const response = await fetch(
|
|
"/api/invite",
|
|
{
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify(formData),
|
|
},
|
|
);
|
|
|
|
if (response.ok) {
|
|
const token = await response.text();
|
|
|
|
// Show success message with token
|
|
tokenDisplay.textContent = token;
|
|
successMessage.classList.add("show");
|
|
submitButton.innerHTML = "Token Generated!";
|
|
} else {
|
|
const error = await response.text();
|
|
throw new Error(error || "Token generation failed");
|
|
}
|
|
} catch (error) {
|
|
console.error("Token generation error:", error);
|
|
alert(
|
|
error.message ||
|
|
"Failed to generate token. Please try again.",
|
|
);
|
|
submitButton.disabled = false;
|
|
submitButton.innerHTML = "Generate Token";
|
|
}
|
|
});
|
|
|
|
// Allow Enter key to submit
|
|
Object.values(form).forEach((input) => {
|
|
if (input.tagName === "INPUT") {
|
|
input.addEventListener("keypress", function (e) {
|
|
if (e.key === "Enter") {
|
|
submitButton.click();
|
|
}
|
|
});
|
|
}
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|