Compare commits

...

5 Commits

Author SHA1 Message Date
adminsopel
50395e90bb Albumik 0.1.2 logo account and password change 2026-05-01 10:32:53 +02:00
adminsopel
33bf33d636 Albumik 0.1.1 stable upgrade path 2026-05-01 10:22:31 +02:00
adminsopel
096bd5976b Fix login flow after rollback 2026-05-01 10:13:50 +02:00
adminsopel
96e1fb2a4e Fix Albumik frontend assets and YunoHost upgrade scripts 2026-05-01 10:05:55 +02:00
adminsopel
077fa00f3e Fix upgrade script to deploy new files 2026-05-01 10:00:43 +02:00
10 changed files with 115 additions and 641 deletions

Binary file not shown.

View File

@@ -1,10 +1,9 @@
packaging_format = 2 packaging_format = 2
id = "albumik" id = "albumik"
name = "Albumik" name = "Albumik"
logo = "assets/albumik-logo.png"
description.en = "Lightweight private photo album with folder permissions and guest uploads" description.en = "Lightweight private photo album with folder permissions and guest uploads"
description.pl = "Lekki prywatny album zdjęć z katalogami, gośćmi i akceptacją zdjęć" description.pl = "Lekki prywatny album zdjęć z katalogami, gośćmi i akceptacją zdjęć"
version = "0.1.1~ynh1" version = "0.1.2~ynh1"
maintainers = ["Filip"] maintainers = ["Filip"]
[upstream] [upstream]

View File

@@ -1,6 +1,7 @@
#!/bin/bash #!/bin/bash
set -euo pipefail set -euo pipefail
source ./_common.sh SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/_common.sh"
mkdir -p "$YNH_BACKUP_DIR" mkdir -p "$YNH_BACKUP_DIR"
tar -C / -czf "$YNH_BACKUP_DIR/albumik-data.tar.gz" "${data_dir#/}" "${config_dir#/}" "${install_dir#/}" 2>/dev/null || true tar -C / -czf "$YNH_BACKUP_DIR/albumik-data.tar.gz" "${data_dir#/}" "${config_dir#/}" "${install_dir#/}" 2>/dev/null || true

View File

@@ -1,6 +1,7 @@
#!/bin/bash #!/bin/bash
set -euo pipefail set -euo pipefail
source ./_common.sh SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/_common.sh"
# YunoHost install args # YunoHost install args
# packaging v2 passes these variables to scripts. # packaging v2 passes these variables to scripts.

View File

@@ -1,6 +1,7 @@
#!/bin/bash #!/bin/bash
set -euo pipefail set -euo pipefail
source ./_common.sh SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/_common.sh"
domain=$(yunohost app setting "$app" domain 2>/dev/null || echo "") domain=$(yunohost app setting "$app" domain 2>/dev/null || echo "")

View File

@@ -1,6 +1,7 @@
#!/bin/bash #!/bin/bash
set -euo pipefail set -euo pipefail
source ./_common.sh SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/_common.sh"
if [ -f "$YNH_BACKUP_DIR/albumik-data.tar.gz" ]; then if [ -f "$YNH_BACKUP_DIR/albumik-data.tar.gz" ]; then
tar -C / -xzf "$YNH_BACKUP_DIR/albumik-data.tar.gz" tar -C / -xzf "$YNH_BACKUP_DIR/albumik-data.tar.gz"

View File

@@ -1,13 +1,22 @@
#!/bin/bash #!/bin/bash
set -euo pipefail set -euo pipefail
source ./_common.sh SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/_common.sh"
app="${YNH_APP_INSTANCE_NAME:-albumik}"
systemctl stop "$app" 2>/dev/null || true
mkdir -p "$install_dir" mkdir -p "$install_dir"
rm -rf "$install_dir/backend" "$install_dir/web" "$install_dir/doc" rm -rf "$install_dir/backend" "$install_dir/web" "$install_dir/doc"
cp -a backend web doc "$install_dir"/
cp -a "$YNH_APP_BASEDIR/backend" "$YNH_APP_BASEDIR/web" "$YNH_APP_BASEDIR/doc" "$install_dir"/
chown -R "$app:$app" "$install_dir" chown -R "$app:$app" "$install_dir"
systemctl daemon-reload systemctl daemon-reload
systemctl start "$app" systemctl restart "$app"
systemctl reload nginx || true
nginx -t
systemctl reload nginx
echo "Albumik upgraded" echo "Albumik upgraded"

View File

@@ -155,7 +155,7 @@ $('#loginForm').addEventListener('submit', async e=>{
e.preventDefault(); e.preventDefault();
$('#loginError').textContent = ''; $('#loginError').textContent = '';
const fd = new FormData(e.currentTarget); const fd = new FormData(e.currentTarget);
try { await api('/api/login', {method:'POST', body: JSON.stringify(Object.fromEntries(fd))}); await init(); } try { await api('/api/login', {method:'POST', body: JSON.stringify(Object.fromEntries(fd))}); window.location.href = '/'; return; }
catch(err){ $('#loginError').textContent = err.message; } catch(err){ $('#loginError').textContent = err.message; }
}); });
$('#logoutBtn').addEventListener('click', async()=>{ await api('/api/logout',{method:'POST',body:'{}'}); location.reload(); }); $('#logoutBtn').addEventListener('click', async()=>{ await api('/api/logout',{method:'POST',body:'{}'}); location.reload(); });
@@ -222,27 +222,43 @@ $('#rejectPhotoBtn').addEventListener('click', async()=>{
init(); init();
function renderAccountView() { /* Albumik 0.1.2 - Moje konto */
const main = document.querySelector("#content") || document.querySelector("main") || document.querySelector(".main"); function albumikEscape(value) {
return String(value ?? "")
.replaceAll("&", "&")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll('"', "&quot;")
.replaceAll("'", "&#039;");
}
function albumikRenderAccountView() {
const main =
document.querySelector("#content") ||
document.querySelector(".content") ||
document.querySelector("main") ||
document.querySelector(".main");
if (!main) return; if (!main) return;
const user = state.user || {}; const user = window.state?.user || state?.user || {};
main.innerHTML = ` main.innerHTML = `
<div class="header"> <div class="header">
<h1>Moje konto</h1> <h1>Moje konto</h1>
<p>Zmień swoje hasło i sprawdź informacje o koncie.</p> <p>Zmień hasło i sprawdź podstawowe informacje o swoim koncie.</p>
</div> </div>
<div class="card account-card" style="padding:24px;margin-top:24px;"> <div class="card account-card" style="padding:24px;margin-top:24px;">
<h2>Dane konta</h2> <h2>Dane konta</h2>
<div class="list-row"><strong>Login</strong><span>${escapeHtml(user.username || '')}</span></div> <div class="list-row"><strong>Login</strong><span>${albumikEscape(user.username || "")}</span></div>
<div class="list-row"><strong>Nazwa</strong><span>${escapeHtml(user.display_name || '')}</span></div> <div class="list-row"><strong>Nazwa</strong><span>${albumikEscape(user.display_name || "")}</span></div>
<div class="list-row"><strong>Rola</strong><span>${escapeHtml(user.role || '')}</span></div> <div class="list-row"><strong>Rola</strong><span>${albumikEscape(user.role || "")}</span></div>
</div> </div>
<div class="card account-card" style="padding:24px;margin-top:18px;"> <div class="card account-card" style="padding:24px;margin-top:18px;">
<h2>Zmiana hasła</h2> <h2>Zmiana hasła</h2>
<form id="changePasswordForm"> <form id="changePasswordForm" method="post">
<div class="field"> <div class="field">
<label>Obecne hasło</label> <label>Obecne hasło</label>
<input name="current_password" type="password" required /> <input name="current_password" type="password" required />
@@ -263,12 +279,15 @@ function renderAccountView() {
document.querySelector("#changePasswordForm")?.addEventListener("submit", async (e) => { document.querySelector("#changePasswordForm")?.addEventListener("submit", async (e) => {
e.preventDefault(); e.preventDefault();
const fd = new FormData(e.currentTarget); const fd = new FormData(e.currentTarget);
const current_password = fd.get("current_password"); const current_password = fd.get("current_password");
const new_password = fd.get("new_password"); const new_password = fd.get("new_password");
const repeat_password = fd.get("repeat_password"); const repeat_password = fd.get("repeat_password");
const err = document.querySelector("#changePasswordError"); const err = document.querySelector("#changePasswordError");
err.style.color = "";
if (new_password !== repeat_password) { if (new_password !== repeat_password) {
err.textContent = "Nowe hasła nie są takie same."; err.textContent = "Nowe hasła nie są takie same.";
return; return;
@@ -294,21 +313,18 @@ function renderAccountView() {
}); });
} }
function escapeHtml(value) {
return String(value ?? "")
.replaceAll("&", "&amp;")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll('"', "&quot;")
.replaceAll("'", "&#039;");
}
document.addEventListener("click", (e) => { document.addEventListener("click", (e) => {
const btn = e.target.closest("[data-view='account']"); const btn = e.target.closest('[data-view="account"]');
if (!btn) return; if (!btn) return;
e.preventDefault(); e.preventDefault();
try {
state.currentView = "account"; state.currentView = "account";
document.querySelectorAll(".nav-btn").forEach(b => b.classList.remove("active")); } catch (e) {}
document.querySelectorAll(".nav-btn").forEach((b) => b.classList.remove("active"));
btn.classList.add("active"); btn.classList.add("active");
renderAccountView();
albumikRenderAccountView();
}); });

View File

@@ -4,15 +4,15 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Albumik</title> <title>Albumik</title>
<link rel="stylesheet" href="/styles.css?v=11" /> <link rel="stylesheet" href="/styles.css" />
</head> </head>
<body> <body>
<div id="login" class="login-shell hidden"> <div id="login" class="login-shell hidden">
<div class="login-card"> <div class="login-card">
<img class="login-logo-small" src="/assets/albumik-logo.png" alt="Albumik" /> <img class="login-logo" src="/assets/albumik-logo.png" alt="Albumik" />
<h1>Albumik</h1> <h1>Albumik</h1>
<p>Lekki prywatny album zdjęć na Twoim serwerze.</p> <p>Lekki prywatny album zdjęć na Twoim serwerze.</p>
<form id="loginForm"> <form id="loginForm" method="post">
<label>Login</label> <label>Login</label>
<input name="username" autocomplete="username" placeholder="admin" required /> <input name="username" autocomplete="username" placeholder="admin" required />
<label>Hasło</label> <label>Hasło</label>
@@ -21,7 +21,6 @@
<div id="loginError" class="error-line"></div> <div id="loginError" class="error-line"></div>
</form> </form>
</div> </div>
</section>
</div> </div>
<div id="app" class="app hidden"> <div id="app" class="app hidden">
@@ -192,6 +191,6 @@
</div> </div>
</dialog> </dialog>
<script src="/app.js?v=11"></script> <script src="/app.js"></script>
</body> </body>
</html> </html>

File diff suppressed because one or more lines are too long