- Added inline edit for redirection rule
This commit is contained in:
Toby Chui 2025-02-05 20:24:42 +08:00
parent bb2d0d5b46
commit 2a9d87787d
4 changed files with 158 additions and 10 deletions

View File

@ -88,6 +88,7 @@ func RegisterRedirectionAPIs(authRouter *auth.RouterDef) {
authRouter.HandleFunc("/api/redirect/list", handleListRedirectionRules)
authRouter.HandleFunc("/api/redirect/add", handleAddRedirectionRule)
authRouter.HandleFunc("/api/redirect/delete", handleDeleteRedirectionRule)
authRouter.HandleFunc("/api/redirect/edit", handleEditRedirectionRule)
authRouter.HandleFunc("/api/redirect/regex", handleToggleRedirectRegexpSupport)
}

View File

@ -2,7 +2,6 @@ package redirection
import (
"encoding/json"
"fmt"
"log"
"os"
"path"
@ -111,6 +110,42 @@ func (t *RuleTable) AddRedirectRule(redirectURL string, destURL string, forwardP
return nil
}
// Edit an existing redirection rule, the oldRedirectURL is used to find the rule to be edited
func (t *RuleTable) EditRedirectRule(oldRedirectURL string, newRedirectURL string, destURL string, forwardPathname bool, statusCode int) error {
newRule := &RedirectRules{
RedirectURL: newRedirectURL,
TargetURL: destURL,
ForwardChildpath: forwardPathname,
StatusCode: statusCode,
}
//Remove the old rule
t.DeleteRedirectRule(oldRedirectURL)
// Convert the redirectURL to a valid filename by replacing "/" with "-" and "." with "_"
filename := utils.ReplaceSpecialCharacters(newRedirectURL) + ".json"
filepath := path.Join(t.configPath, filename)
// Create a new file for writing the JSON data
file, err := os.Create(filepath)
if err != nil {
t.log("Error creating file "+filepath, err)
return err
}
defer file.Close()
err = json.NewEncoder(file).Encode(newRule)
if err != nil {
t.log("Error encoding JSON to file "+filepath, err)
return err
}
// Update the runtime map
t.rules.Store(newRedirectURL, newRule)
return nil
}
func (t *RuleTable) DeleteRedirectRule(redirectURL string) error {
// Convert the redirectURL to a valid filename by replacing "/" with "-" and "." with "_"
filename := utils.ReplaceSpecialCharacters(redirectURL) + ".json"
@ -118,7 +153,6 @@ func (t *RuleTable) DeleteRedirectRule(redirectURL string) error {
// Create the full file path by joining the t.configPath with the filename
filepath := path.Join(t.configPath, filename)
fmt.Println(redirectURL, filename, filepath)
// Check if the file exists
if _, err := os.Stat(filepath); os.IsNotExist(err) {
return nil // File doesn't exist, nothing to delete

View File

@ -78,6 +78,49 @@ func handleDeleteRedirectionRule(w http.ResponseWriter, r *http.Request) {
utils.SendOK(w)
}
func handleEditRedirectionRule(w http.ResponseWriter, r *http.Request) {
originalRedirectUrl, err := utils.PostPara(r, "originalRedirectUrl")
if err != nil {
utils.SendErrorResponse(w, "original redirect url cannot be empty")
return
}
newRedirectUrl, err := utils.PostPara(r, "newRedirectUrl")
if err != nil {
utils.SendErrorResponse(w, "redirect url cannot be empty")
return
}
destUrl, err := utils.PostPara(r, "destUrl")
if err != nil {
utils.SendErrorResponse(w, "destination url cannot be empty")
}
forwardChildpath, err := utils.PostPara(r, "forwardChildpath")
if err != nil {
//Assume true
forwardChildpath = "true"
}
redirectTypeString, err := utils.PostPara(r, "redirectType")
if err != nil {
redirectTypeString = "307"
}
redirectionStatusCode, err := strconv.Atoi(redirectTypeString)
if err != nil {
utils.SendErrorResponse(w, "invalid status code number")
return
}
err = redirectTable.EditRedirectRule(originalRedirectUrl, newRedirectUrl, destUrl, forwardChildpath == "true", redirectionStatusCode)
if err != nil {
utils.SendErrorResponse(w, err.Error())
return
}
utils.SendOK(w)
}
// Toggle redirection regex support. Note that this cost another O(n) time complexity to each page load
func handleToggleRedirectRegexpSupport(w http.ResponseWriter, r *http.Request) {
enabled, err := utils.PostPara(r, "enable")

View File

@ -13,7 +13,7 @@
<th>Destination URL</th>
<th class="no-sort">Copy Pathname</th>
<th class="no-sort">Status Code</th>
<th class="no-sort">Remove</th>
<th class="no-sort">Actions</th>
</tr>
</thead>
<tbody id="redirectionRuleList">
@ -163,13 +163,21 @@
$("#redirectionRuleList").html("");
$.get("/api/redirect/list", function(data){
data.forEach(function(entry){
$("#redirectionRuleList").append(`<tr>
<td><a href="${entry.RedirectURL}" target="_blank">${entry.RedirectURL}</a></td>
<td>${entry.TargetURL}</td>
<td>${entry.ForwardChildpath?"<i class='ui green checkmark icon'></i>":"<i class='ui red remove icon'></i>"}</td>
<td>${entry.StatusCode==307?"Temporary Redirect (307)":"Moved Permanently (301)"}</td>
<td><button onclick="deleteRule(this);" rurl="${encodeURIComponent(JSON.stringify(entry.RedirectURL))}" title="Delete redirection rule" class="ui mini red icon basic button"><i class="trash icon"></i></button></td>
</tr>`);
let encodedEntry = encodeURIComponent(JSON.stringify(entry));
let hrefURL = entry.RedirectURL;
if (!hrefURL.startsWith("http")){
hrefURL = "https://" + hrefURL;
}
$("#redirectionRuleList").append(`<tr>
<td><a href="${hrefURL}" target="_blank">${entry.RedirectURL}</a></td>
<td>${entry.TargetURL}</td>
<td>${entry.ForwardChildpath?"<i class='ui green checkmark icon'></i>":"<i class='ui red remove icon'></i>"}</td>
<td>${entry.StatusCode==307?"Temporary Redirect (307)":"Moved Permanently (301)"}</td>
<td>
<button onclick="editRule(this);" payload="${encodedEntry}" title="Edit redirection rule" class="ui mini blue icon basic button redirectEditBtn"><i class="edit icon"></i></button>
<button onclick="deleteRule(this);" rurl="${encodeURIComponent(JSON.stringify(entry.RedirectURL))}" title="Delete redirection rule" class="ui mini red icon basic button"><i class="trash icon"></i></button>
</td>
</tr>`);
});
if (data.length == 0){
@ -180,6 +188,68 @@
}
initRedirectionRuleList();
function editRule(obj){
$(".redirectEditBtn").addClass("disabled");
let payload = JSON.parse(decodeURIComponent($(obj).attr("payload")));
let row = $(obj).closest("tr");
let redirectUrl = payload.RedirectURL;
let destUrl = payload.TargetURL;
let forwardChildpath = payload.ForwardChildpath;
let statusCode = payload.StatusCode;
row.html(`
<td>
<div class="ui small input">
<input type="text" value="${redirectUrl}" id="editRedirectUrl">
</div>
</td>
<td>
<div class="ui small input">
<input type="text" value="${destUrl}" id="editDestUrl">
</div>
</td>
<td><div class="ui toggle checkbox"><input type="checkbox" ${forwardChildpath ? "checked" : ""} id="editForwardChildpath"><label></label></div></td>
<td>
<div class="ui radio checkbox"><input type="radio" name="editStatusCode" value="307" ${statusCode == 307 ? "checked" : ""}><label>Temporary Redirect (307)</label></div><br>
<div class="ui radio checkbox"><input type="radio" name="editStatusCode" value="301" ${statusCode == 301 ? "checked" : ""}><label>Moved Permanently (301)</label></div>
</td>
<td>
<button onclick="saveEditRule(this);" payload="${encodeURIComponent(JSON.stringify(payload))}" class="ui mini green icon basic button"><i class="save icon"></i></button>
<button onclick="initRedirectionRuleList();" class="ui mini icon basic button"><i class="cancel icon"></i></button>
</td>
`);
$(".checkbox").checkbox();
}
function saveEditRule(obj){
let payload = JSON.parse(decodeURIComponent($(obj).attr("payload")));
let redirectUrl = $("#editRedirectUrl").val();
let destUrl = $("#editDestUrl").val();
let forwardChildpath = $("#editForwardChildpath").is(":checked");
let statusCode = parseInt($("input[name='editStatusCode']:checked").val());
$.cjax({
url: "/api/redirect/edit",
method: "POST",
data: {
originalRedirectUrl: payload.RedirectURL,
newRedirectUrl: redirectUrl,
destUrl: destUrl,
forwardChildpath: forwardChildpath,
redirectType: statusCode,
},
success: function(data){
if (data.error != undefined){
msgbox(data.error, false);
}else{
msgbox("Redirection rule updated", true);
initRedirectionRuleList();
}
}
});
}
function initRegexpSupportToggle(){
$.get("/api/redirect/regex", function(data){
//Set the checkbox initial state