Release 202506111039
This commit is contained in:
@@ -333,28 +333,32 @@ def remove_service_attribute_by_slug(service_id: int, attribute_slug: str) -> bo
|
|||||||
|
|
||||||
|
|
||||||
def save_tos_review(service_id: int, review: Optional[TosReviewType]):
|
def save_tos_review(service_id: int, review: Optional[TosReviewType]):
|
||||||
"""
|
"""Persist a TOS review and/or update the timestamp for a service.
|
||||||
Save a TOS review for a specific service.
|
|
||||||
|
|
||||||
Args:
|
If *review* is ``None`` the existing review (if any) is preserved while
|
||||||
service_id: The ID of the service.
|
only the ``tosReviewAt`` column is updated. This ensures we still track
|
||||||
review: A TypedDict containing the review data.
|
when the review task last ran even if the review generation failed or
|
||||||
|
produced no changes.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# Only serialize to JSON if review is not None
|
|
||||||
review_json = json.dumps(review) if review is not None else None
|
|
||||||
with get_db_connection() as conn:
|
with get_db_connection() as conn:
|
||||||
with conn.cursor(row_factory=dict_row) as cursor:
|
with conn.cursor(row_factory=dict_row) as cursor:
|
||||||
cursor.execute(
|
if review is None:
|
||||||
"""
|
cursor.execute(
|
||||||
UPDATE "Service"
|
'UPDATE "Service" SET "tosReviewAt" = NOW() WHERE id = %s AND "tosReview" IS NULL',
|
||||||
SET "tosReview" = %s, "tosReviewAt" = NOW()
|
(service_id,),
|
||||||
WHERE id = %s
|
)
|
||||||
""",
|
else:
|
||||||
(review_json, service_id),
|
review_json = json.dumps(review)
|
||||||
)
|
cursor.execute(
|
||||||
|
'UPDATE "Service" SET "tosReview" = %s, "tosReviewAt" = NOW() WHERE id = %s',
|
||||||
|
(review_json, service_id),
|
||||||
|
)
|
||||||
|
|
||||||
conn.commit()
|
conn.commit()
|
||||||
logger.info(f"Successfully saved TOS review for service {service_id}")
|
logger.info(
|
||||||
|
f"Successfully saved TOS review (updated={review is not None}) for service {service_id}"
|
||||||
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error saving TOS review for service {service_id}: {e}")
|
logger.error(f"Error saving TOS review for service {service_id}: {e}")
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ type EventTypeInfo<T extends string | null | undefined = string> = {
|
|||||||
description: string
|
description: string
|
||||||
classNames: {
|
classNames: {
|
||||||
dot: string
|
dot: string
|
||||||
|
banner?: string
|
||||||
}
|
}
|
||||||
icon: string
|
icon: string
|
||||||
color: TailwindColor
|
color: TailwindColor
|
||||||
@@ -34,6 +35,7 @@ export const {
|
|||||||
description: '',
|
description: '',
|
||||||
classNames: {
|
classNames: {
|
||||||
dot: 'bg-zinc-700 text-zinc-300 ring-zinc-700/50',
|
dot: 'bg-zinc-700 text-zinc-300 ring-zinc-700/50',
|
||||||
|
banner: 'bg-zinc-900/50 text-zinc-300 hover:bg-zinc-800/60 focus-visible:bg-zinc-800/60',
|
||||||
},
|
},
|
||||||
icon: 'ri:question-fill',
|
icon: 'ri:question-fill',
|
||||||
color: 'gray',
|
color: 'gray',
|
||||||
@@ -48,6 +50,7 @@ export const {
|
|||||||
description: 'Potential issues that users should be aware of',
|
description: 'Potential issues that users should be aware of',
|
||||||
classNames: {
|
classNames: {
|
||||||
dot: 'bg-amber-900 text-amber-300 ring-amber-900/50',
|
dot: 'bg-amber-900 text-amber-300 ring-amber-900/50',
|
||||||
|
banner: 'bg-yellow-900/50 text-yellow-300 hover:bg-yellow-800/60 focus-visible:bg-yellow-800/60',
|
||||||
},
|
},
|
||||||
icon: 'ri:alert-fill',
|
icon: 'ri:alert-fill',
|
||||||
color: 'yellow',
|
color: 'yellow',
|
||||||
@@ -61,6 +64,7 @@ export const {
|
|||||||
description: 'A previously reported warning has been solved',
|
description: 'A previously reported warning has been solved',
|
||||||
classNames: {
|
classNames: {
|
||||||
dot: 'bg-amber-900 text-amber-300 ring-amber-900/50',
|
dot: 'bg-amber-900 text-amber-300 ring-amber-900/50',
|
||||||
|
banner: 'bg-yellow-900/50 text-yellow-300 hover:bg-yellow-800/60 focus-visible:bg-yellow-800/60',
|
||||||
},
|
},
|
||||||
icon: 'ri:alert-fill',
|
icon: 'ri:alert-fill',
|
||||||
color: 'green',
|
color: 'green',
|
||||||
@@ -74,6 +78,7 @@ export const {
|
|||||||
description: 'Critical issues affecting service functionality',
|
description: 'Critical issues affecting service functionality',
|
||||||
classNames: {
|
classNames: {
|
||||||
dot: 'bg-red-900 text-red-300 ring-red-900/50',
|
dot: 'bg-red-900 text-red-300 ring-red-900/50',
|
||||||
|
banner: 'bg-red-900/50 text-red-300 hover:bg-red-800/60 focus-visible:bg-red-800/60',
|
||||||
},
|
},
|
||||||
icon: 'ri:spam-fill',
|
icon: 'ri:spam-fill',
|
||||||
color: 'red',
|
color: 'red',
|
||||||
@@ -87,6 +92,7 @@ export const {
|
|||||||
description: 'A previously reported alert has been solved',
|
description: 'A previously reported alert has been solved',
|
||||||
classNames: {
|
classNames: {
|
||||||
dot: 'bg-red-900 text-red-300 ring-red-900/50',
|
dot: 'bg-red-900 text-red-300 ring-red-900/50',
|
||||||
|
banner: 'bg-red-900/50 text-red-300 hover:bg-red-800/60 focus-visible:bg-red-800/60',
|
||||||
},
|
},
|
||||||
icon: 'ri:spam-fill',
|
icon: 'ri:spam-fill',
|
||||||
color: 'green',
|
color: 'green',
|
||||||
@@ -100,6 +106,7 @@ export const {
|
|||||||
description: 'General information about the service',
|
description: 'General information about the service',
|
||||||
classNames: {
|
classNames: {
|
||||||
dot: 'bg-blue-900 text-blue-300 ring-blue-900/50',
|
dot: 'bg-blue-900 text-blue-300 ring-blue-900/50',
|
||||||
|
banner: 'bg-blue-900/50 text-blue-300 hover:bg-blue-800/60 focus-visible:bg-blue-800/60',
|
||||||
},
|
},
|
||||||
icon: 'ri:information-fill',
|
icon: 'ri:information-fill',
|
||||||
color: 'sky',
|
color: 'sky',
|
||||||
@@ -113,6 +120,7 @@ export const {
|
|||||||
description: 'Regular service update or announcement',
|
description: 'Regular service update or announcement',
|
||||||
classNames: {
|
classNames: {
|
||||||
dot: 'bg-zinc-700 text-zinc-300 ring-zinc-700/50',
|
dot: 'bg-zinc-700 text-zinc-300 ring-zinc-700/50',
|
||||||
|
banner: 'bg-zinc-900/50 text-zinc-300 hover:bg-zinc-800/60 focus-visible:bg-zinc-800/60',
|
||||||
},
|
},
|
||||||
icon: 'ri:notification-fill',
|
icon: 'ri:notification-fill',
|
||||||
color: 'green',
|
color: 'green',
|
||||||
@@ -126,6 +134,7 @@ export const {
|
|||||||
description: 'Service details were updated on kycnot.me',
|
description: 'Service details were updated on kycnot.me',
|
||||||
classNames: {
|
classNames: {
|
||||||
dot: 'bg-sky-900 text-sky-300 ring-sky-900/50',
|
dot: 'bg-sky-900 text-sky-300 ring-sky-900/50',
|
||||||
|
banner: 'bg-sky-900/50 text-sky-300 hover:bg-sky-800/60 focus-visible:bg-sky-800/60',
|
||||||
},
|
},
|
||||||
icon: 'ri:pencil-fill',
|
icon: 'ri:pencil-fill',
|
||||||
color: 'sky',
|
color: 'sky',
|
||||||
|
|||||||
@@ -407,9 +407,12 @@ const ogImageTemplateData = {
|
|||||||
|
|
||||||
const serviceVisibilityInfo = getServiceVisibilityInfo(service.serviceVisibility)
|
const serviceVisibilityInfo = getServiceVisibilityInfo(service.serviceVisibility)
|
||||||
|
|
||||||
const activeAlertOrWarningEvents = service.events.filter(
|
const activeAlertOrWarningEvents = service.events
|
||||||
(event) => getEventTypeInfo(event.type).showBanner && (event.endedAt === null || event.endedAt >= now)
|
.map((event) => ({
|
||||||
)
|
...event,
|
||||||
|
typeInfo: getEventTypeInfo(event.type),
|
||||||
|
}))
|
||||||
|
.filter((event) => event.typeInfo.showBanner && (event.endedAt === null || event.endedAt >= now))
|
||||||
const activeEventToShow =
|
const activeEventToShow =
|
||||||
activeAlertOrWarningEvents.find((event) => event.type === EventType.ALERT) ?? activeAlertOrWarningEvents[0]
|
activeAlertOrWarningEvents.find((event) => event.type === EventType.ALERT) ?? activeAlertOrWarningEvents[0]
|
||||||
---
|
---
|
||||||
@@ -518,15 +521,10 @@ const activeEventToShow =
|
|||||||
href="#events"
|
href="#events"
|
||||||
class={cn(
|
class={cn(
|
||||||
'group mb-4 block rounded-md px-3 py-2 text-sm transition-colors duration-200',
|
'group mb-4 block rounded-md px-3 py-2 text-sm transition-colors duration-200',
|
||||||
activeEventToShow.type === EventType.ALERT
|
activeEventToShow.typeInfo.classNames.banner
|
||||||
? 'bg-red-900/50 text-red-300 hover:bg-red-800/60 focus-visible:bg-red-800/60'
|
|
||||||
: 'bg-yellow-900/50 text-yellow-300 hover:bg-yellow-800/60 focus-visible:bg-yellow-800/60'
|
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon name={activeEventToShow.typeInfo.icon} class="me-1.5 inline-block size-4 align-[-0.15em]" />
|
||||||
name={activeEventToShow.type === EventType.ALERT ? 'ri:alert-fill' : 'ri:alarm-warning-fill'}
|
|
||||||
class="me-1.5 inline-block size-4 align-[-0.15em]"
|
|
||||||
/>
|
|
||||||
<span class="font-bold">{activeEventToShow.title}</span> — {activeEventToShow.content}
|
<span class="font-bold">{activeEventToShow.title}</span> — {activeEventToShow.content}
|
||||||
{activeAlertOrWarningEvents.length >= 2 && <>+{activeAlertOrWarningEvents.length - 1} more events.</>}
|
{activeAlertOrWarningEvents.length >= 2 && <>+{activeAlertOrWarningEvents.length - 1} more events.</>}
|
||||||
<span class="underline">Go to events</span>
|
<span class="underline">Go to events</span>
|
||||||
@@ -1070,6 +1068,9 @@ const activeEventToShow =
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<p class="text-day-400 mt-3 text-center text-xs">
|
||||||
|
<span class="hover:text-day-200 transition-colors">Overall = 60% Privacy + 40% Trust (Rounded)</span>
|
||||||
|
</p>
|
||||||
<div class="xs:gap-x-6 mt-2 flex flex-wrap justify-center gap-x-4 gap-y-2 text-xs">
|
<div class="xs:gap-x-6 mt-2 flex flex-wrap justify-center gap-x-4 gap-y-2 text-xs">
|
||||||
<a
|
<a
|
||||||
href="/about#service-scores"
|
href="/about#service-scores"
|
||||||
@@ -1083,7 +1084,7 @@ const activeEventToShow =
|
|||||||
class="text-day-400 hover:text-day-200 inline-flex items-center gap-1 transition-colors hover:underline"
|
class="text-day-400 hover:text-day-200 inline-flex items-center gap-1 transition-colors hover:underline"
|
||||||
>
|
>
|
||||||
<Icon name="ri:information-line" class="size-3" />
|
<Icon name="ri:information-line" class="size-3" />
|
||||||
Attributes list
|
All attributes list
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1194,15 +1195,15 @@ const activeEventToShow =
|
|||||||
<p class="text-day-500 mt-1 text-sm text-balance">
|
<p class="text-day-500 mt-1 text-sm text-balance">
|
||||||
Maybe due to captchas, client side rendering, DDoS protections, or non-text format.
|
Maybe due to captchas, client side rendering, DDoS protections, or non-text format.
|
||||||
</p>
|
</p>
|
||||||
{service.tosUrls.length > 0 && (
|
<p class="mt-2 text-xs">
|
||||||
<p class="mt-2 text-xs">
|
Reviewed <TimeFormatted date={service.tosReviewAt} hourPrecision />
|
||||||
{service.tosUrls.map((url) => (
|
{service.tosUrls.length > 0 && 'from'}
|
||||||
<a href={url} class="hover:underline">
|
{service.tosUrls.map((url) => (
|
||||||
{urlDomain(url)}
|
<a href={url} class="hover:underline">
|
||||||
</a>
|
{urlDomain(url)}
|
||||||
))}
|
</a>
|
||||||
</p>
|
))}
|
||||||
)}
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
Reference in New Issue
Block a user