Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
927ec08
fix(search-menu-left-site) deleted the search menu from left menu, be…
NorbertAguilera Jun 3, 2026
9d60a09
fix(footer): added mailto to the footer email
Bogdan2005-19 Jun 5, 2026
216b108
Merge pull request #183 from Null-Pointers2-0/179-fixfooter-make-emai…
gery25 Jun 5, 2026
4179d85
fix(info-card): moved the info card to user_client file
gery25 Jun 6, 2026
33ba597
style(profile-tab):redifined the visual tab
gery25 Jun 6, 2026
1db00b6
Merge branch 'dev' into 176-styleprofile-add-colored-borders-and-visu…
gery25 Jun 6, 2026
541ff74
fix(dashboard): renamed the grafics to more understanding
gery25 Jun 6, 2026
fe63507
Merge branch 'dev' into 177-refactorsidebar-remove-non-functional-sea…
NorbertAguilera Jun 7, 2026
3059c07
Update quality-pipeline.yml to solve errors
NorbertAguilera Jun 7, 2026
5a13e00
Update quality-pipeline.yml
NorbertAguilera Jun 7, 2026
325b170
fix(ci): exclude tests/venv from static scan and fix hardcoded password
NorbertAguilera Jun 7, 2026
470f76d
Merge branch '177-refactorsidebar-remove-non-functional-search-input'…
NorbertAguilera Jun 7, 2026
bc04d3f
fix: added old configurations
NorbertAguilera Jun 7, 2026
b745797
Merge pull request #185 from Null-Pointers2-0/180-fixdashboard-rename…
NorbertAguilera Jun 7, 2026
22e0181
Merge pull request #186 from Null-Pointers2-0/177-refactorsidebar-rem…
gery25 Jun 7, 2026
ec23072
feat(history): update backend logic with date grouping, pagination, a…
Karu174 Jun 8, 2026
e5d54bc
feat(history): redesign UI with horizontal cards, filters, and groupe…
Karu174 Jun 8, 2026
eefdc98
fix(history): avoid duplicate entries on platform click and clean up UI
Karu174 Jun 8, 2026
b9e960b
Merge pull request #188 from Null-Pointers2-0/feat/history-redesign
gery25 Jun 8, 2026
3125f78
feat(recommendations): fix duplicates and implement carousel in serie…
Karu174 Jun 9, 2026
6243618
Merge pull request #189 from Null-Pointers2-0/feat/recommendations
Bogdan2005-19 Jun 9, 2026
0624ce4
feat(admin): add system logs tab and restrict platform tracking to lo…
Karu174 Jun 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions .github/workflows/quality-pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -645,7 +645,7 @@ jobs:
FAIL=0

if find . \( -name "*.js" -o -name "*.ts" -o -name "*.vue" -o -name "*.py" -o -name "*.java" \) \
-not -path "*/node_modules/*" -not -path "*/dist/*" 2>/dev/null | grep -q .; then
-not -path "*/node_modules/*" -not -path "*/dist/*" -not -path "*/tests/*" -not -path "*/.venv/*" 2>/dev/null | grep -q .; then
DETECTED=1
else
DETECTED=0
Expand All @@ -654,22 +654,22 @@ jobs:
# Unconditional frontend router guard bypass
if grep -rEn "next\(true\)|beforeEach.*return true|navigate\(.*\{.*replace.*\}\)" \
--include="*.js" --include="*.ts" --include="*.vue" . 2>/dev/null \
| grep -v node_modules | grep -v "//"; then
| grep -vE "node_modules|tests/|\.venv|dist/" | grep -v "//"; then
echo "::warning::Possible unconditional navigation guard bypass detected"
fi

# Hardcoded passwords / tokens
if grep -rEn "password\s*[=:]\s*['\"][^'\"]{4,}['\"]" \
--include="*.js" --include="*.ts" --include="*.vue" --include="*.py" --include="*.java" \
. 2>/dev/null \
| grep -v node_modules \
| grep -vE "node_modules|tests/|\.venv|dist/" \
| grep -v "//" \
| grep -vE "[[:space:]]:[a-zA-Z][a-zA-Z0-9_-]*=" \
| grep -q .; then
grep -rEn "password\s*[=:]\s*['\"][^'\"]{4,}['\"]" \
--include="*.js" --include="*.ts" --include="*.vue" --include="*.py" --include="*.java" \
. 2>/dev/null \
| grep -v node_modules | grep -v "//" \
| grep -vE "node_modules|tests/|\.venv|dist/" | grep -v "//" \
| grep -vE "[[:space:]]:[a-zA-Z][a-zA-Z0-9_-]*="
echo "::error::Hardcoded password value detected in source code"
FAIL=1
Expand All @@ -678,22 +678,22 @@ jobs:
# eval() / exec() usage
if grep -rEn "\beval\(|\bexec\(" \
--include="*.js" --include="*.ts" --include="*.vue" --include="*.py" \
. 2>/dev/null | grep -v node_modules | grep -v "//"; then
. 2>/dev/null | grep -vE "node_modules|tests/|\.venv|dist/" | grep -v "//"; then
echo "::warning::eval()/exec() usage detected — review for potential code injection risk"
fi

# Disabled SSL/TLS certificate verification
if grep -rEn "rejectUnauthorized\s*:\s*false|verify\s*=\s*False|ssl_verify\s*=\s*false" \
--include="*.js" --include="*.ts" --include="*.py" --include="*.java" \
. 2>/dev/null | grep -v node_modules | grep -v "//"; then
. 2>/dev/null | grep -vE "node_modules|tests/|\.venv|dist/" | grep -v "//"; then
echo "::error::SSL/TLS certificate verification disabled — man-in-the-middle attack risk"
FAIL=1
fi

# Sensitive tokens in URL query strings
if grep -rEn "[?&](token|api_key|apikey|access_token|secret)=" \
--include="*.js" --include="*.ts" --include="*.vue" --include="*.py" \
. 2>/dev/null | grep -v node_modules | grep -v "//"; then
. 2>/dev/null | grep -vE "node_modules|tests/|\.venv|dist/" | grep -v "//"; then
echo "::warning::Sensitive token/key passed in URL query string — use Authorization header instead."
fi

Expand Down
105 changes: 65 additions & 40 deletions Templates/Details/details_base.html
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ <h1 class="main-title">{{ content.title }}</h1>
data-contingut-id="{{ content.contingut.id }}"
data-api-id="{{ api.id }}">
{{ api.name|default:api.port }}
<span class="api-stats"></span>
</button>
{% empty %}
<span style="font-size: 12px; color: var(--tm);">{% trans "No hay información de API disponible." %}</span>
Expand Down Expand Up @@ -137,15 +136,35 @@ <h4>{% trans "Deja tu opinión" %}</h4>
<div id="history-toast" class="toast-notification">✅ {% trans "Añadido a tu historial" %}</div>

<script>
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}

document.addEventListener('DOMContentLoaded', () => {
// 1. Lógica del botón de favoritos (Seguir / Dejar de seguir)
const favoriteBtn = document.getElementById('btn-save');
if (favoriteBtn) {
favoriteBtn.addEventListener('click', function() {
const targetUrl = this.getAttribute('data-url');
const csrftoken = getCookie('csrftoken');

fetch(targetUrl, {
method: 'POST',
headers: { 'X-CSRFToken': '{{ csrf_token }}', 'Content-Type': 'application/json' }
headers: {
'X-CSRFToken': csrftoken,
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
}
})
.then(response => response.json())
.then(data => {
Expand All @@ -155,44 +174,50 @@ <h4>{% trans "Deja tu opinión" %}</h4>
});
}

// 2. Lógica de tracking asíncrono de clics en plataformas (Issue #24)
const platformButtons = document.querySelectorAll('.track-platform-btn');
const toast = document.getElementById('history-toast');

platformButtons.forEach(btn => {
btn.addEventListener('click', function() {
const contingutId = this.getAttribute('data-contingut-id');
const apiId = this.getAttribute('data-api-id');
const statsSpan = this.querySelector('.api-stats');

fetch("{% url 'register_platform_click' %}", {
method: 'POST',
headers: {
'X-CSRFToken': '{{ csrf_token }}',
'Content-Type': 'application/json'
},
body: JSON.stringify({ contingut_id: contingutId, api_id: apiId })
})
.then(response => {
if (!response.ok) throw new Error('Error en el registro del clic');
return response.json();
})
.then(data => {
if (data.status === 'success') {
// Feedback visual inmediato en el botón e inyección de la métrica
this.classList.add('clicked');
statsSpan.innerText = `(${data.total_clicks} views)`;

// Disparar el banner flotante (Toast)
toast.classList.add('show');
setTimeout(() => {
toast.classList.remove('show');
}, 3000);
}
})
.catch(error => console.error('Error tracking click:', error));
// 2. Lógica de plataformas según si el usuario está logueado o no
{% if user.is_authenticated %}
// Si ESTÁ logueado: Rastreamos el clic y mostramos el Toast
const platformButtons = document.querySelectorAll('.track-platform-btn');
const toast = document.getElementById('history-toast');

platformButtons.forEach(btn => {
btn.addEventListener('click', function() {
const contingutId = this.getAttribute('data-contingut-id');
const apiId = this.getAttribute('data-api-id');
const csrftoken = getCookie('csrftoken');

fetch("{% url 'register_platform_click' %}", {
method: 'POST',
headers: {
'X-CSRFToken': csrftoken,
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
},
body: JSON.stringify({ contingut_id: contingutId, api_id: apiId })
})
.then(response => {
if (!response.ok) throw new Error('Error en el registro del clic');
return response.json();
})
.then(data => {
if (data.status === 'success') {
toast.classList.add('show');
setTimeout(() => {
toast.classList.remove('show');
}, 3000);
}
})
.catch(error => console.error('Error tracking click:', error));
});
});
{% else %}
const platformButtons = document.querySelectorAll('.track-platform-btn');
platformButtons.forEach(btn => {
btn.addEventListener('click', function() {
window.location.href = "{% url 'login' %}";
});
});
});
{% endif %}
});
</script>
{% endblock %}
25 changes: 24 additions & 1 deletion Templates/Details/details_serie.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,27 @@
{% endif %}
{% endblock %}

{% block favorite_url %}{% url 'toggle_series_favorite' content.id %}{% endblock %}
{% block favorite_url %}{% url 'toggle_series_favorite' content.id %}{% endblock %}

{% block extra_main_content %}
<div class="recommendations-section" style="margin: 30px 0;">
<h3 class="section-title">{% trans "También te puede gustar..." %}</h3>
<div class="recommendations-grid" style="display: flex; gap: 15px; overflow-x: auto; padding-bottom: 10px;">
{% for rec in recommendations %}
<a href="{% url 'series_detail' rec.id %}" style="text-decoration: none; color: white; min-width: 140px;">
{% if rec.poster_path %}
<img src="{{ rec.poster_path }}" alt="{{ rec.title }}" style="width: 140px; height: 210px; object-fit: cover; border-radius: 8px;">
{% else %}
<div style="width: 140px; height: 210px; background: rgba(255,255,255,0.05); border-radius: 8px; display: flex; align-items: center; justify-content: center; text-align: center; padding: 10px;">
{{ rec.title }}
</div>
{% endif %}
<p style="font-size: 0.9rem; margin-top: 8px; font-weight: bold; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">{{ rec.title }}</p>
</a>
{% empty %}
<!-- Mensaje de Fallback -->
<p style="color: rgba(255,255,255,0.5);">{% trans "No hay recomendaciones disponibles." %}</p>
{% endfor %}
</div>
</div>
{% endblock %}
9 changes: 7 additions & 2 deletions Templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@
</a>
</div>
{% endif %}

<!--
<div class="menu-search">
<div class="search-input-container">
<svg class="search-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor"
Expand All @@ -136,6 +136,7 @@
<input type="text" class="search-input" placeholder="{% trans 'Search...' %}">
</div>
</div>
-->
</nav>

<!-- Overlay -->
Expand Down Expand Up @@ -174,7 +175,11 @@ <h5>{% trans "Legal" %}</h5>
</div>
<div class="col-md-3 mb-4">
<h5>{% trans "Contact" %}</h5>
<p>info@StreamSync.com</p>
<p>
<a href="mailto:info@StreamSync.com">
info@StreamSync.com
</a>
</p>
<p>+34 600 000 000</p>
</div>
</div>
Expand Down
44 changes: 44 additions & 0 deletions Templates/paginator.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{% load i18n %}

<nav aria-label="Page navigation">
<ul class="pagination justify-content-center mb-0 custom-pagination">
{% if items.has_previous %}
<li class="page-item">
<a class="page-link text-white-50 bg-transparent border-secondary" href="?page=1{% if request.GET.platform %}&platform={{ request.GET.platform }}{% endif %}{% if request.GET.date %}&date={{ request.GET.date }}{% endif %}" aria-label="{% trans 'Primera' %}">
<span aria-hidden="true">&laquo;&laquo;</span>
</a>
</li>
<li class="page-item">
<a class="page-link text-white-50 bg-transparent border-secondary" href="?page={{ items.previous_page_number }}{% if request.GET.platform %}&platform={{ request.GET.platform }}{% endif %}{% if request.GET.date %}&date={{ request.GET.date }}{% endif %}" aria-label="{% trans 'Anterior' %}">
<span aria-hidden="true">&laquo;</span>
</a>
</li>
{% endif %}

<li class="page-item active" aria-current="page">
<span class="page-link text-neon bg-dark border-secondary" style="border-color: var(--neon-cyan) !important;">
{% trans "Pàgina" %} {{ items.number }} {% trans "de" %} {{ items.paginator.num_pages }}
</span>
</li>

{% if items.has_next %}
<li class="page-item">
<a class="page-link text-white-50 bg-transparent border-secondary" href="?page={{ items.next_page_number }}{% if request.GET.platform %}&platform={{ request.GET.platform }}{% endif %}{% if request.GET.date %}&date={{ request.GET.date }}{% endif %}" aria-label="{% trans 'Següent' %}">
<span aria-hidden="true">&raquo;</span>
</a>
</li>
<li class="page-item">
<a class="page-link text-white-50 bg-transparent border-secondary" href="?page={{ items.paginator.num_pages }}{% if request.GET.platform %}&platform={{ request.GET.platform }}{% endif %}{% if request.GET.date %}&date={{ request.GET.date }}{% endif %}" aria-label="{% trans 'Última' %}">
<span aria-hidden="true">&raquo;&raquo;</span>
</a>
</li>
{% endif %}
</ul>
</nav>

<style>
.custom-pagination .page-link:hover {
background-color: rgba(102, 252, 241, 0.1) !important;
color: var(--neon-cyan) !important;
}
</style>
1 change: 1 addition & 0 deletions users/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ def __call__(self, request):
'update_user_role',
'gestion_cartelleres',
'editar_cartellera',
'system_logs',
]

if current_url_name not in exempt_url_names and not request.path.startswith('/static/'):
Expand Down
Loading
Loading