Release 202507010740
This commit is contained in:
835
web/package-lock.json
generated
835
web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -31,26 +31,26 @@
|
||||
"@astrojs/rss": "4.0.12",
|
||||
"@astrojs/sitemap": "3.4.1",
|
||||
"@fontsource-variable/space-grotesk": "5.2.8",
|
||||
"@fontsource/inter": "5.2.5",
|
||||
"@fontsource/inter": "5.2.6",
|
||||
"@fontsource/space-grotesk": "5.2.8",
|
||||
"@prisma/client": "6.9.0",
|
||||
"@tailwindcss/vite": "4.1.8",
|
||||
"@types/mime-types": "3.0.0",
|
||||
"@prisma/client": "6.10.1",
|
||||
"@tailwindcss/vite": "4.1.11",
|
||||
"@types/mime-types": "3.0.1",
|
||||
"@types/pg": "8.15.4",
|
||||
"@vercel/og": "0.6.8",
|
||||
"astro": "5.9.0",
|
||||
"astro": "5.10.1",
|
||||
"astro-loading-indicator": "0.7.0",
|
||||
"astro-remote": "0.3.4",
|
||||
"astro-seo-schema": "5.0.0",
|
||||
"canvas": "3.1.0",
|
||||
"canvas": "3.1.2",
|
||||
"clsx": "2.1.1",
|
||||
"htmx.org": "1.9.12",
|
||||
"htmx.org": "2.0.6",
|
||||
"javascript-time-ago": "2.5.11",
|
||||
"libphonenumber-js": "1.12.9",
|
||||
"lodash-es": "4.17.21",
|
||||
"mime-types": "3.0.1",
|
||||
"object-to-formdata": "4.5.1",
|
||||
"pg": "8.16.0",
|
||||
"pg": "8.16.3",
|
||||
"qrcode": "1.5.4",
|
||||
"react": "19.1.0",
|
||||
"redis": "5.5.6",
|
||||
@@ -58,52 +58,51 @@
|
||||
"seedrandom": "3.0.5",
|
||||
"sharp": "0.34.2",
|
||||
"slugify": "1.6.6",
|
||||
"tailwind-merge": "3.3.0",
|
||||
"tailwind-merge": "3.3.1",
|
||||
"tailwind-variants": "1.0.0",
|
||||
"tailwindcss": "4.1.8",
|
||||
"tailwindcss": "4.1.11",
|
||||
"typescript": "5.8.3",
|
||||
"unique-username-generator": "1.4.0",
|
||||
"web-push": "3.6.7",
|
||||
"zod-form-data": "2.0.7"
|
||||
"web-push": "3.6.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "9.28.0",
|
||||
"@eslint/js": "9.30.0",
|
||||
"@faker-js/faker": "9.8.0",
|
||||
"@iconify-json/material-symbols": "1.2.24",
|
||||
"@iconify-json/material-symbols": "1.2.28",
|
||||
"@iconify-json/mdi": "1.2.3",
|
||||
"@iconify-json/ri": "1.2.5",
|
||||
"@stylistic/eslint-plugin": "4.4.1",
|
||||
"@stylistic/eslint-plugin": "5.1.0",
|
||||
"@tailwindcss/forms": "0.5.10",
|
||||
"@tailwindcss/typography": "0.5.16",
|
||||
"@types/eslint__js": "9.14.0",
|
||||
"@types/lodash-es": "4.17.12",
|
||||
"@types/qrcode": "1.5.5",
|
||||
"@types/react": "19.1.6",
|
||||
"@types/react": "19.1.8",
|
||||
"@types/seedrandom": "3.0.8",
|
||||
"@types/web-push": "3.6.4",
|
||||
"@typescript-eslint/parser": "8.33.1",
|
||||
"@typescript-eslint/parser": "8.35.1",
|
||||
"@vite-pwa/assets-generator": "1.0.0",
|
||||
"@vite-pwa/astro": "1.1.0",
|
||||
"astro-icon": "1.1.5",
|
||||
"date-fns": "4.1.0",
|
||||
"esbuild": "0.25.5",
|
||||
"eslint": "9.28.0",
|
||||
"eslint-import-resolver-typescript": "4.4.3",
|
||||
"eslint": "9.30.0",
|
||||
"eslint-import-resolver-typescript": "4.4.4",
|
||||
"eslint-plugin-astro": "1.3.1",
|
||||
"eslint-plugin-import": "2.31.0",
|
||||
"eslint-plugin-import": "2.32.0",
|
||||
"eslint-plugin-jsx-a11y": "6.10.2",
|
||||
"globals": "16.2.0",
|
||||
"prettier": "3.5.3",
|
||||
"prettier": "3.6.2",
|
||||
"prettier-plugin-astro": "0.14.1",
|
||||
"prettier-plugin-tailwindcss": "0.6.12",
|
||||
"prisma": "6.9.0",
|
||||
"prisma-json-types-generator": "3.4.2",
|
||||
"prettier-plugin-tailwindcss": "0.6.13",
|
||||
"prisma": "6.10.1",
|
||||
"prisma-json-types-generator": "3.5.0",
|
||||
"tailwind-htmx": "0.1.2",
|
||||
"ts-essentials": "10.0.4",
|
||||
"ts-essentials": "10.1.1",
|
||||
"ts-toolbelt": "9.6.0",
|
||||
"tsx": "4.19.4",
|
||||
"typescript-eslint": "8.33.1",
|
||||
"vite-plugin-devtools-json": "0.1.1",
|
||||
"tsx": "4.20.3",
|
||||
"typescript-eslint": "8.35.1",
|
||||
"vite-plugin-devtools-json": "0.2.1",
|
||||
"workbox-core": "7.3.0",
|
||||
"workbox-precaching": "7.3.0"
|
||||
}
|
||||
|
||||
@@ -165,6 +165,7 @@ export const serviceSuggestionActions = {
|
||||
message: 'You must accept the suggestion rules and process to continue',
|
||||
}),
|
||||
}),
|
||||
operatingSince: z.coerce.date().optional(),
|
||||
/** @deprecated Honey pot field, do not use */
|
||||
message: z.unknown().optional(),
|
||||
skipDuplicateCheck: z
|
||||
@@ -239,6 +240,7 @@ export const serviceSuggestionActions = {
|
||||
name: input.name,
|
||||
slug: input.slug,
|
||||
description: input.description,
|
||||
operatingSince: input.operatingSince,
|
||||
serviceUrls,
|
||||
tosUrls: input.tosUrls,
|
||||
onionUrls,
|
||||
|
||||
24
web/src/components/AdminNavigationFixScript.astro
Normal file
24
web/src/components/AdminNavigationFixScript.astro
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
if (!Astro.locals.user?.admin) return
|
||||
---
|
||||
|
||||
<script>
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Script that adds data-astro-reload to all admin links or all links if on an admin page. //
|
||||
// This is a workaround to prevent the client router messing up inputs. //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
document.addEventListener('astro:page-load', () => {
|
||||
document.querySelectorAll<HTMLAnchorElement | HTMLFormElement>('a,form').forEach((element) => {
|
||||
const isAdminPage = window.location.pathname.startsWith('/admin')
|
||||
if (isAdminPage) {
|
||||
element.setAttribute('data-astro-reload', '')
|
||||
}
|
||||
|
||||
const url = element.href ? new URL(element.href) : null
|
||||
if (url?.pathname.startsWith('/admin')) {
|
||||
element.setAttribute('data-astro-reload', '')
|
||||
}
|
||||
})
|
||||
})
|
||||
</script>
|
||||
@@ -8,6 +8,7 @@ import { pwaInfo } from 'virtual:pwa-info'
|
||||
import { isNotArray } from '../lib/arrays'
|
||||
import { DEPLOYMENT_MODE } from '../lib/client/envVariables'
|
||||
|
||||
import AdminNavigationFixScript from './AdminNavigationFixScript.astro'
|
||||
import DevToolsMessageScript from './DevToolsMessageScript.astro'
|
||||
import DynamicFavicon from './DynamicFavicon.astro'
|
||||
import HtmxScript from './HtmxScript.astro'
|
||||
@@ -108,6 +109,7 @@ const ogImageUrl = makeOgImageUrl(ogImage, Astro.url)
|
||||
<DynamicFavicon />
|
||||
|
||||
<ClientRouter />
|
||||
<AdminNavigationFixScript />
|
||||
|
||||
<LoadingIndicator color="green" />
|
||||
<TailwindJsPluggin />
|
||||
|
||||
@@ -6,7 +6,6 @@ import { serviceVisibilitiesById } from '../constants/serviceVisibility'
|
||||
import { verificationStatusesByValue } from '../constants/verificationStatus'
|
||||
import { cn } from '../lib/cn'
|
||||
import { makeOverallScoreInfo } from '../lib/overallScore'
|
||||
import { transformCase } from '../lib/strings'
|
||||
|
||||
import MyPicture from './MyPicture.astro'
|
||||
import Tooltip from './Tooltip.astro'
|
||||
@@ -23,6 +22,8 @@ type Props = HTMLAttributes<'a'> & {
|
||||
slug: true
|
||||
description: true
|
||||
overallScore: true
|
||||
privacyScore: true
|
||||
trustScore: true
|
||||
kycLevel: true
|
||||
imageUrl: true
|
||||
verificationStatus: true
|
||||
@@ -45,6 +46,8 @@ const {
|
||||
slug,
|
||||
description,
|
||||
overallScore,
|
||||
privacyScore,
|
||||
trustScore,
|
||||
kycLevel,
|
||||
imageUrl,
|
||||
categories,
|
||||
@@ -163,7 +166,7 @@ const overallScoreInfo = makeOverallScoreInfo(overallScore)
|
||||
'inline-flex size-6 items-center justify-center rounded-sm text-lg font-bold',
|
||||
overallScoreInfo.classNameBg
|
||||
)}
|
||||
text={`${transformCase(overallScoreInfo.text, 'sentence')} score (${overallScoreInfo.formattedScore}/10)`}
|
||||
text={`${Math.round(privacyScore).toLocaleString()}% Privacy | ${Math.round(trustScore).toLocaleString()}% Trust`}
|
||||
>
|
||||
{overallScoreInfo.formattedScore}
|
||||
</Tooltip>
|
||||
|
||||
@@ -268,13 +268,12 @@ export const nonDbAttributes: NonDbAttributeFull[] = [
|
||||
trustPoints: -4,
|
||||
links: [],
|
||||
customize: (service) => {
|
||||
const started = service.operatingSince as unknown as Date | null
|
||||
if (!started) return { show: false }
|
||||
if (!service.operatingSince) return { show: false }
|
||||
|
||||
const yearsOperated = differenceInYears(new Date(), started)
|
||||
const yearsOperated = differenceInYears(new Date(), service.operatingSince)
|
||||
if (yearsOperated >= 1) return { show: false }
|
||||
|
||||
const monthsOperated = differenceInMonths(new Date(), started)
|
||||
const monthsOperated = differenceInMonths(new Date(), service.operatingSince)
|
||||
return {
|
||||
show: true,
|
||||
description: `The service started operations ${
|
||||
@@ -296,10 +295,9 @@ export const nonDbAttributes: NonDbAttributeFull[] = [
|
||||
trustPoints: 5,
|
||||
links: [],
|
||||
customize: (service) => {
|
||||
const started = service.operatingSince as unknown as Date | null
|
||||
if (!started) return { show: false }
|
||||
if (!service.operatingSince) return { show: false }
|
||||
|
||||
const yearsOperated = differenceInYears(new Date(), started)
|
||||
const yearsOperated = differenceInYears(new Date(), service.operatingSince)
|
||||
return {
|
||||
show: yearsOperated >= 2,
|
||||
description: `This service has been operational for **${String(
|
||||
|
||||
@@ -45,7 +45,6 @@ function prismaClientSingleton() {
|
||||
}
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line no-var
|
||||
var prisma: ReturnType<typeof prismaClientSingleton> | undefined
|
||||
}
|
||||
|
||||
|
||||
@@ -367,13 +367,8 @@ const apiCalls = await Astro.locals.banners.try(
|
||||
description="Date the service started operating"
|
||||
inputProps={{
|
||||
type: 'date',
|
||||
value: service.operatingSince
|
||||
? new Date(
|
||||
service.operatingSince.getTime() - service.operatingSince.getTimezoneOffset() * 60000
|
||||
)
|
||||
.toISOString()
|
||||
.slice(0, 10)
|
||||
: '',
|
||||
value: service.operatingSince?.toISOString().slice(0, 10),
|
||||
max: new Date().toISOString().slice(0, 10),
|
||||
}}
|
||||
error={serviceInputErrors.operatingSince}
|
||||
/>
|
||||
|
||||
@@ -15,13 +15,11 @@ import KarmaUnlocksTable from '../../components/KarmaUnlocksTable.astro'
|
||||
There are several ways to earn karma points:
|
||||
|
||||
1. **Comment Approval** (+1 point)
|
||||
|
||||
- When your comment moves from 'unmoderated' to 'approved' status.
|
||||
- This is the basic reward for contributing a valid comment.
|
||||
- Users related to the service (e.g. owners, admins, etc.) do not get karma for their comments.
|
||||
|
||||
2. **Comment Verification** (+5 points)
|
||||
|
||||
- When your comment is marked as 'verified'.
|
||||
- This is a significant reward for providing particularly valuable or verified information.
|
||||
|
||||
@@ -44,7 +42,6 @@ The system also includes penalties to discourage spam and low-quality content:
|
||||
The system maintains a detailed record of all karma changes through:
|
||||
|
||||
1. **Karma Transactions**
|
||||
|
||||
- Every karma change is recorded as a transaction.
|
||||
- Each transaction includes:
|
||||
- The action that triggered it.
|
||||
|
||||
@@ -238,27 +238,29 @@ const [categories, attributes] = await Astro.locals.banners.tryMany([
|
||||
/>
|
||||
</div>
|
||||
|
||||
<InputTextArea
|
||||
label="ToS URLs"
|
||||
description="One per line. AI review uses the first working URL only."
|
||||
name="tosUrls"
|
||||
inputProps={{
|
||||
placeholder: 'example.com/tos',
|
||||
required: true,
|
||||
class: 'min-h-10',
|
||||
}}
|
||||
error={inputErrors.tosUrls}
|
||||
/>
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<InputTextArea
|
||||
label="ToS URLs"
|
||||
description="One per line. AI review uses the first working URL only."
|
||||
name="tosUrls"
|
||||
inputProps={{
|
||||
placeholder: 'example.com/tos',
|
||||
required: true,
|
||||
class: 'min-h-10',
|
||||
}}
|
||||
error={inputErrors.tosUrls}
|
||||
/>
|
||||
|
||||
<InputText
|
||||
label="Operating since"
|
||||
name="operatingSince"
|
||||
description="Date the service started operating"
|
||||
inputProps={{
|
||||
type: 'date',
|
||||
}}
|
||||
error={(inputErrors as Record<string, unknown>).operatingSince}
|
||||
/>
|
||||
<InputText
|
||||
label="Operating since"
|
||||
name="operatingSince"
|
||||
inputProps={{
|
||||
type: 'date',
|
||||
max: new Date().toISOString().slice(0, 10),
|
||||
}}
|
||||
error={inputErrors.operatingSince}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<InputCardGroup
|
||||
name="kycLevel"
|
||||
@@ -321,6 +323,7 @@ const [categories, attributes] = await Astro.locals.banners.tryMany([
|
||||
icon: [attribute.categoryInfo.icon, attribute.typeInfo.icon],
|
||||
iconClassName: [attribute.categoryInfo.classNames.icon, attribute.typeInfo.classNames.icon],
|
||||
}))}
|
||||
description="See list of [all attributes](/attributes) and their scoring."
|
||||
error={inputErrors.attributes}
|
||||
size="lg"
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user