add Edit function

This commit is contained in:
minoplhy 2024-10-31 23:08:51 +07:00
parent 201d0b319f
commit abb48c68c5
Signed by: minoplhy
GPG Key ID: 41D406044E2434BF
6 changed files with 132 additions and 12 deletions

View File

@ -77,6 +77,15 @@ pub fn delete_link(shortlink: String, db: &Connection) -> bool {
} }
} }
// Edit Existing Long link.
pub fn edit_link(shortlink: String, longlink: String, db: &Connection) -> bool {
db.execute(
"UPDATE urls SET long_url = ?1 WHERE short_url = ?2;",
[longlink, shortlink],
)
.is_ok()
}
// Open the DB, and create schema if missing // Open the DB, and create schema if missing
pub fn open_db(path: String) -> Connection { pub fn open_db(path: String) -> Connection {
let db = Connection::open(path).expect("Unable to open database!"); let db = Connection::open(path).expect("Unable to open database!");

View File

@ -63,6 +63,7 @@ async fn main() -> Result<()> {
.service(services::siteurl) .service(services::siteurl)
.service(services::version) .service(services::version)
.service(services::add_link) .service(services::add_link)
.service(services::edit_link)
.service(services::delete_link) .service(services::delete_link)
.service(services::login) .service(services::login)
.service(services::logout) .service(services::logout)

View File

@ -4,11 +4,7 @@
use actix_files::NamedFile; use actix_files::NamedFile;
use actix_session::Session; use actix_session::Session;
use actix_web::{ use actix_web::{
delete, get, delete, get, http::StatusCode, post, put, web::{self, Redirect}, Either, HttpResponse, Responder
http::StatusCode,
post,
web::{self, Redirect},
Either, HttpResponse, Responder,
}; };
use std::env; use std::env;
@ -128,6 +124,26 @@ pub async fn logout(session: Session) -> HttpResponse {
} }
} }
// Edit link
#[put("/api/edit/{shortlink}")]
pub async fn edit_link(
req: String,
shortlink: web::Path<String>,
data: web::Data<AppState>,
session: Session,
) -> HttpResponse {
if env::var("public_mode") == Ok(String::from("Enable")) || auth::validate(session) {
let out = utils::edit_link(req, shortlink.to_string(), &data.db);
if out.0 {
HttpResponse::Created().body(out.1)
} else {
HttpResponse::Conflict().body(out.1)
}
} else {
HttpResponse::Unauthorized().body("Not logged in!")
}
}
// Delete a given shortlink // Delete a given shortlink
#[delete("/api/del/{shortlink}")] #[delete("/api/del/{shortlink}")]
pub async fn delete_link( pub async fn delete_link(
@ -144,4 +160,4 @@ pub async fn delete_link(
} else { } else {
HttpResponse::Unauthorized().body("Not logged in!") HttpResponse::Unauthorized().body("Not logged in!")
} }
} }

View File

@ -17,6 +17,11 @@ struct URLPair {
longlink: String, longlink: String,
} }
#[derive(Deserialize)]
struct EditLinkJson {
longlink: String,
}
// Request the DB for searching an URL // Request the DB for searching an URL
pub fn get_longurl(shortlink: String, db: &Connection) -> Option<String> { pub fn get_longurl(shortlink: String, db: &Connection) -> Option<String> {
if validate_link(&shortlink) { if validate_link(&shortlink) {
@ -76,6 +81,46 @@ pub fn add_link(req: String, db: &Connection) -> (bool, String) {
} }
} }
// Make Check then edit the longurl link
pub fn edit_link(req: String, shortlink: String, db: &Connection) -> (bool, String) {
let chunks: EditLinkJson;
if let Ok(json) = serde_json::from_str(&req) {
chunks = json;
} else {
// shorturl should always be supplied, even if empty
return (false, String::from("Invalid request!"));
}
if shortlink.is_empty() {
(false, String::from("Invaild edit parameter received."));
}
if longurl_compares(shortlink.clone(), chunks.longlink.clone(), db)
{
(
database::edit_link(shortlink.clone(), chunks.longlink, db),
shortlink,
)
} else {
(
false,
String::from("Long/Short URL not valid or already in use!"),
)
}
}
// Doing Longurl check(Type None or existed?)
pub fn longurl_compares(shorturl: String, longurl:String, db: &Connection) -> bool {
if get_longurl(shorturl.clone(), db).is_none() {
return false;
}
if get_longurl(shorturl.clone(), db).unwrap() == longurl {
return false;
}
return true;
}
// Check if link, and request DB to delete it if exists // Check if link, and request DB to delete it if exists
pub fn delete_link(shortlink: String, db: &Connection) -> bool { pub fn delete_link(shortlink: String, db: &Connection) -> bool {
if validate_link(shortlink.as_str()) { if validate_link(shortlink.as_str()) {

View File

@ -35,10 +35,10 @@
<input type="url" name="longUrl" id="longUrl" placeholder="Please enter a valid URL" <input type="url" name="longUrl" id="longUrl" placeholder="Please enter a valid URL"
onblur="addProtocol(this)" required /> onblur="addProtocol(this)" required />
</div> </div>
<div class=" pure-control-group"> <div class="pure-control-group">
<label for="shortUrl">Short URL (optional)</label> <label for="shortUrl">Short URL (optional)</label>
<input type="text" name="shortUrl" id="shortUrl" placeholder="Only a-z, 0-9, - and _ are allowed" <input type="text" name="shortUrl" id="shortUrl" placeholder="Only a-z, 0-9, - and _ are allowed"
pattern="[a-z0-9\-_]+" title="Only a-z, 0-9, - and _ are allowed"/> pattern="[a-z0-9\-_]+" title="Only a-z, 0-9, - and _ are allowed" />
</div> </div>
<div class="pure-controls" id="controls"> <div class="pure-controls" id="controls">
<button class="pure-button pure-button-primary">Shorten!</button> <button class="pure-button pure-button-primary">Shorten!</button>
@ -56,6 +56,7 @@
<td id="short-url-header">Short URL (click to copy)</td> <td id="short-url-header">Short URL (click to copy)</td>
<td>Long URL</td> <td>Long URL</td>
<td name="hitsColumn">Hits</td> <td name="hitsColumn">Hits</td>
<td name="editBtn">Edit</td>
<td name="deleteBtn">&times;</td> <td name="deleteBtn">&times;</td>
</tr> </tr>
</thead> </thead>

View File

@ -105,19 +105,20 @@ const TR = (row, site) => {
var shortTD = null; var shortTD = null;
if (window.isSecureContext) { if (window.isSecureContext) {
shortTD = TD(A_SHORT(row["shortlink"], site), "Short URL"); shortTD = TD(A_SHORT(row["shortlink"], site), "Short URL");
} } else {
else {
shortTD = TD(A_SHORT_INSECURE(row["shortlink"], site), "Short URL"); shortTD = TD(A_SHORT_INSECURE(row["shortlink"], site), "Short URL");
} }
let hitsTD = TD(row["hits"]); let hitsTD = TD(row["hits"]);
hitsTD.setAttribute("label", "Hits"); hitsTD.setAttribute("label", "Hits");
hitsTD.setAttribute("name", "hitsColumn"); hitsTD.setAttribute("name", "hitsColumn");
const btn = deleteButton(row["shortlink"]); const deleteBtn = deleteButton(row["shortlink"]);
const editBtn = editButton(row["shortlink"]);
tr.appendChild(shortTD); tr.appendChild(shortTD);
tr.appendChild(longTD); tr.appendChild(longTD);
tr.appendChild(hitsTD); tr.appendChild(hitsTD);
tr.appendChild(btn); tr.appendChild(editBtn);
tr.appendChild(deleteBtn);
return tr; return tr;
} }
@ -178,6 +179,53 @@ const deleteButton = (shortUrl) => {
return td; return td;
} }
const editButton = (shortUrl) => {
const td = document.createElement("td");
const btn = document.createElement("button");
btn.innerHTML = "Edit";
btn.onclick = async (e) => {
e.preventDefault();
const newUrl = prompt("Enter the new long URL:");
if (newUrl) {
if (!isValidUrl(newUrl)) {
showAlert("Invalid URL format. Please enter a valid URL.", "red");
return;
}
const response = await fetch(prepSubdir(`/api/edit/${shortUrl}`), {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ longlink: newUrl }),
});
if (response.ok) {
showAlert(`Successfully updated ${shortUrl}.`, "green");
refreshData();
} else {
const errorMsg = await response.text();
showAlert(`Error: ${errorMsg}`, "red");
}
}
};
td.setAttribute("name", "editBtn");
td.appendChild(btn);
return td;
}
const isValidUrl = (urlString) => {
try {
new URL(urlString);
return true;
} catch (_) {
return false;
}
}
const TD = (s, u) => { const TD = (s, u) => {
const td = document.createElement("td"); const td = document.createElement("td");
const div = document.createElement("div"); const div = document.createElement("div");