Compare commits
No commits in common. "e0a74dc23d84b8d6de4cc5bbc505ec154b25682a" and "9b52e821aa03e2a4825d63cf9659cc3cb0855cb2" have entirely different histories.
e0a74dc23d
...
9b52e821aa
12 changed files with 909 additions and 911 deletions
|
|
@ -50,7 +50,7 @@ msgstr "Ver la fuente del artículo"
|
||||||
|
|
||||||
#: src/snapshots/templates/article-template.html.j2:138
|
#: src/snapshots/templates/article-template.html.j2:138
|
||||||
msgid "You are leaving this page"
|
msgid "You are leaving this page"
|
||||||
msgstr "Estás abandonando esta pagina"
|
msgstr "Estas abandonando esta pagina"
|
||||||
|
|
||||||
#: src/snapshots/templates/article-template.html.j2:157
|
#: src/snapshots/templates/article-template.html.j2:157
|
||||||
msgid ""
|
msgid ""
|
||||||
|
|
|
||||||
1746
poetry.lock
generated
1746
poetry.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -8,7 +8,7 @@ license = "BSD-2"
|
||||||
package-mode = false
|
package-mode = false
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = "^3.13"
|
python = "^3.14"
|
||||||
alembic = "^1.18.4"
|
alembic = "^1.18.4"
|
||||||
babel = "^2.17"
|
babel = "^2.17"
|
||||||
beautifulsoup4 = "^4.13"
|
beautifulsoup4 = "^4.13"
|
||||||
|
|
@ -28,12 +28,12 @@ tldextract = "^5"
|
||||||
uvicorn = {extras = ["standard"], version = "^0.30.6"}
|
uvicorn = {extras = ["standard"], version = "^0.30.6"}
|
||||||
|
|
||||||
[tool.poetry.group.dev.dependencies]
|
[tool.poetry.group.dev.dependencies]
|
||||||
black = "^26"
|
black = "^25.1.0"
|
||||||
ruff = "^0.12"
|
ruff = "^0.12"
|
||||||
pytest = "^8.4"
|
pytest = "^8.4"
|
||||||
|
|
||||||
[tool.poetry.group.prod.dependencies]
|
[tool.poetry.group.prod.dependencies]
|
||||||
gunicorn = "^26"
|
gunicorn = "^22.0.0"
|
||||||
python-json-logger = "^2.0.7"
|
python-json-logger = "^2.0.7"
|
||||||
prometheus-client = "^0.20.0"
|
prometheus-client = "^0.20.0"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ def get_kaleidoscope_mirror(origin: str) -> str | None:
|
||||||
"TE": "trailers",
|
"TE": "trailers",
|
||||||
}
|
}
|
||||||
response = requests.post(
|
response = requests.post(
|
||||||
"https://kldscpe.info/api/v2/resolve", json=payload, headers=headers, timeout=10
|
"https://kldscpe.info/api/v2/resolve", json=payload, headers=headers
|
||||||
)
|
)
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -14,13 +14,7 @@ router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
@router.get("/api/v1/link")
|
@router.get("/api/v1/link")
|
||||||
def get_link(
|
def get_link(background_tasks: BackgroundTasks, db: DbSession, auth: ApiKey, url: str, type_: str = Query(default="auto", alias="type")):
|
||||||
background_tasks: BackgroundTasks,
|
|
||||||
db: DbSession,
|
|
||||||
auth: ApiKey,
|
|
||||||
url: str,
|
|
||||||
type_: str = Query(default="auto", alias="type"),
|
|
||||||
):
|
|
||||||
if auth and type_ in ["auto", "live", "live-short"]:
|
if auth and type_ in ["auto", "live", "live-short"]:
|
||||||
s = db.query(Link).filter(Link.url == url, Link.pool == 0).first()
|
s = db.query(Link).filter(Link.url == url, Link.pool == 0).first()
|
||||||
if not s and resolve_mirror(db, url):
|
if not s and resolve_mirror(db, url):
|
||||||
|
|
@ -56,11 +50,8 @@ def resolve_hash(db: DbSession, hash_: str, host: str = Header(settings.LINK_DOM
|
||||||
headers={"Referrer-Policy": "no-referrer"},
|
headers={"Referrer-Policy": "no-referrer"},
|
||||||
)
|
)
|
||||||
if host.lower().strip() != settings.API_DOMAIN:
|
if host.lower().strip() != settings.API_DOMAIN:
|
||||||
target = resolve_mirror(db, link.url)
|
|
||||||
if not target:
|
|
||||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
|
|
||||||
return RedirectResponse(
|
return RedirectResponse(
|
||||||
target,
|
resolve_mirror(db, link.url),
|
||||||
status_code=status.HTTP_302_FOUND,
|
status_code=status.HTTP_302_FOUND,
|
||||||
headers={"Referrer-Policy": "no-referrer"},
|
headers={"Referrer-Policy": "no-referrer"},
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import secrets
|
|
||||||
from typing import Annotated
|
from typing import Annotated
|
||||||
|
|
||||||
from fastapi import Depends, Header, HTTPException
|
from fastapi import Depends, Header, HTTPException
|
||||||
|
|
@ -11,10 +10,10 @@ def api_key(host: str = Header(), authorization: str | None = Header(None)) -> b
|
||||||
if host.lower().strip() != settings.API_DOMAIN.strip():
|
if host.lower().strip() != settings.API_DOMAIN.strip():
|
||||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
|
||||||
try:
|
try:
|
||||||
if secrets.compare_digest(authorization.split()[1], settings.API_KEY):
|
if authorization.split()[1] == settings.API_KEY:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
except (AttributeError, TypeError, IndexError):
|
except AttributeError, TypeError, IndexError:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -248,9 +248,9 @@ class SnapshotCamera:
|
||||||
article_description=self.get_attribute_value(
|
article_description=self.get_attribute_value(
|
||||||
'meta[name="description"]', "content", optional=True
|
'meta[name="description"]', "content", optional=True
|
||||||
),
|
),
|
||||||
article_image=(
|
article_image=fetch_url(self.url, article_image_source)
|
||||||
fetch_url(self.url, article_image_source) if article_image_source else None
|
if article_image_source
|
||||||
),
|
else None,
|
||||||
article_image_caption=self.get_element_content(
|
article_image_caption=self.get_element_content(
|
||||||
self.config.article_image_caption_selector, optional=True
|
self.config.article_image_caption_selector, optional=True
|
||||||
),
|
),
|
||||||
|
|
@ -286,7 +286,7 @@ class SnapshotCamera:
|
||||||
package_path="templates",
|
package_path="templates",
|
||||||
),
|
),
|
||||||
extensions=["jinja2.ext.i18n"],
|
extensions=["jinja2.ext.i18n"],
|
||||||
autoescape=True,
|
autoescape=select_autoescape(),
|
||||||
trim_blocks=True,
|
trim_blocks=True,
|
||||||
lstrip_blocks=True,
|
lstrip_blocks=True,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -21,12 +21,7 @@ router = APIRouter()
|
||||||
)
|
)
|
||||||
def context(auth: ApiKey, url: str = "https://www.bbc.com/russian/articles/ckgeey4dqgxo"):
|
def context(auth: ApiKey, url: str = "https://www.bbc.com/russian/articles/ckgeey4dqgxo"):
|
||||||
if settings.ENVIRONMENT.is_debug or auth:
|
if settings.ENVIRONMENT.is_debug or auth:
|
||||||
ctx = SnapshotCamera(url).get_context()
|
return SnapshotCamera(url).get_context()
|
||||||
if ctx is None:
|
|
||||||
raise HTTPException(
|
|
||||||
status.HTTP_404_NOT_FOUND, detail="No configuration for URL"
|
|
||||||
)
|
|
||||||
return ctx
|
|
||||||
raise HTTPException(status.HTTP_404_NOT_FOUND)
|
raise HTTPException(status.HTTP_404_NOT_FOUND)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -38,7 +33,7 @@ def context(auth: ApiKey, url: str = "https://www.bbc.com/russian/articles/ckgee
|
||||||
def parse(auth: ApiKey, url: str = "https://www.bbc.com/russian/articles/ckgeey4dqgxo"):
|
def parse(auth: ApiKey, url: str = "https://www.bbc.com/russian/articles/ckgeey4dqgxo"):
|
||||||
if settings.ENVIRONMENT.is_debug or auth:
|
if settings.ENVIRONMENT.is_debug or auth:
|
||||||
return SnapshotCamera(url).render()
|
return SnapshotCamera(url).render()
|
||||||
raise HTTPException(status.HTTP_403_FORBIDDEN)
|
raise HTTPException(status.HTTP_404_NOT_FOUND)
|
||||||
|
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
|
|
@ -53,15 +48,10 @@ def snap(
|
||||||
):
|
):
|
||||||
s = db.query(Snapshot).filter(Snapshot.url == url, Snapshot.pool == 0).first()
|
s = db.query(Snapshot).filter(Snapshot.url == url, Snapshot.pool == 0).first()
|
||||||
if not s and config_for_url(url):
|
if not s and config_for_url(url):
|
||||||
s = Snapshot(
|
s = Snapshot(url=url, pool=0, snapshot_state=SnapshotState.PENDING, provider=SnapshotProvider.GOOGLE)
|
||||||
url=url,
|
|
||||||
pool=0,
|
|
||||||
snapshot_state=SnapshotState.PENDING,
|
|
||||||
provider=SnapshotProvider.GOOGLE,
|
|
||||||
)
|
|
||||||
db.add(s)
|
db.add(s)
|
||||||
db.commit()
|
db.commit()
|
||||||
background_tasks.add_task(generate_snapshot, s.id)
|
background_tasks.add_task(generate_snapshot, s.id)
|
||||||
if s:
|
if s:
|
||||||
return {"url": s.link}
|
return {"url": s.link}
|
||||||
raise HTTPException(status.HTTP_404_NOT_FOUND)
|
return status.HTTP_403_FORBIDDEN
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ class SnapshotContext(BaseModel):
|
||||||
page_direction: str | None = None
|
page_direction: str | None = None
|
||||||
page_language: str | None = None
|
page_language: str | None = None
|
||||||
site_favicon: str | None = None
|
site_favicon: str | None = None
|
||||||
site_logo: str | None = None
|
site_logo: str = None
|
||||||
site_title: str
|
site_title: str
|
||||||
site_mirror_url: str | None = None
|
site_mirror_url: str | None = None
|
||||||
site_url: str
|
site_url: str
|
||||||
|
|
|
||||||
|
|
@ -19,13 +19,11 @@ def generate_snapshot(id_: int) -> None:
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
content = SnapshotCamera(snapshot.url).render()
|
content = SnapshotCamera(snapshot.url).render()
|
||||||
upload_blob(
|
upload_blob(hashids.encode(snapshot.id) + ".html", content.encode("utf-8"), "text/html")
|
||||||
hashids.encode(snapshot.id) + ".html", content.encode("utf-8"), "text/html"
|
|
||||||
)
|
|
||||||
snapshot.snapshot_state = SnapshotState.UPDATING
|
snapshot.snapshot_state = SnapshotState.UPDATING
|
||||||
snapshot.snapshot_published_at = datetime.now()
|
snapshot.snapshot_published_at = datetime.now()
|
||||||
db.commit()
|
db.commit()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception("Failed to generate snapshot %s", id_)
|
logging.error(e)
|
||||||
snapshot.snapshot_state = SnapshotState.FAILED
|
snapshot.snapshot_state = SnapshotState.FAILED
|
||||||
db.commit()
|
db.commit()
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
<link rel="icon" href="{{ site_favicon }}" />
|
<link rel="icon" href="{{ site_favicon }}" />
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<style>
|
<style>
|
||||||
{{ article_css() | safe }}
|
{{ article_css() }}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
@ -44,9 +44,9 @@
|
||||||
let target = e.target.closest("a");
|
let target = e.target.closest("a");
|
||||||
if (target) {
|
if (target) {
|
||||||
// if the click was on or within an <a>
|
// if the click was on or within an <a>
|
||||||
if (!target.classList.contains("snap-skip-link") &&
|
if (!target.className.includes("snap-skip-link") &&
|
||||||
!target.classList.contains("snap-link--mirror") &&
|
!target.className.includes("snap-link--mirror") &&
|
||||||
!target.classList.contains("snap-link--snapshot")) {
|
!target.className.includes("snap-link--snapshot")) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
document.body.dataset.currentLink = target.href;
|
document.body.dataset.currentLink = target.href;
|
||||||
}
|
}
|
||||||
|
|
@ -124,7 +124,7 @@
|
||||||
</figure>
|
</figure>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="snap-content">
|
<div class="snap-content">
|
||||||
{{ article_body | safe }}
|
{{ article_body }}
|
||||||
{% if article_mirror_url %}
|
{% if article_mirror_url %}
|
||||||
<p>
|
<p>
|
||||||
<a href="{{ article_mirror_url }}" class="snap-footer-link snap-link--mirror">
|
<a href="{{ article_mirror_url }}" class="snap-footer-link snap-link--mirror">
|
||||||
|
|
|
||||||
|
|
@ -171,7 +171,7 @@ figcaption {
|
||||||
margin-top: 3px;
|
margin-top: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.snap-footer-link:dir(ltr) svg {
|
.snap-footer-link svg:dir(ltr) {
|
||||||
float: right;
|
float: right;
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue