diff --git a/web/prisma/seed.ts b/web/prisma/seed.ts
index 2f21e55..4a75079 100755
--- a/web/prisma/seed.ts
+++ b/web/prisma/seed.ts
@@ -916,7 +916,7 @@ const specialUsersData = {
verifiedLink: 'https://kycnot.me',
totalKarma: 1001,
link: 'https://kycnot.me',
- picture: 'https://comments.kycnot.me/api/users/549f290e-0542-4c18-b437-5b64b35758f0/avatar?size=L',
+ picture: 'https://kycnot.me/files/users/pictures/c277dc0f2f.png',
},
moderator: {
name: 'moderator_dev',
@@ -928,7 +928,7 @@ const specialUsersData = {
verifiedLink: 'https://kycnot.me',
totalKarma: 1001,
link: 'https://kycnot.me',
- picture: 'https://comments.kycnot.me/api/users/549f290e-0542-4c18-b437-5b64b35758f0/avatar?size=L',
+ picture: 'https://kycnot.me/files/users/pictures/c277dc0f2f.png',
},
verified: {
name: 'verified_dev',
diff --git a/web/src/actions/admin/service.ts b/web/src/actions/admin/service.ts
index a504dca..9710890 100644
--- a/web/src/actions/admin/service.ts
+++ b/web/src/actions/admin/service.ts
@@ -6,12 +6,8 @@ import slugify from 'slugify'
import { defineProtectedAction } from '../../lib/defineProtectedAction'
import { saveFileLocally } from '../../lib/fileStorage'
import { prisma } from '../../lib/prisma'
-import {
- imageFileSchema,
- stringListOfUrlsSchema,
- stringListOfUrlsSchemaRequired,
- zodCohercedNumber,
-} from '../../lib/zodUtils'
+import { separateServiceUrlsByType } from '../../lib/urls'
+import { imageFileSchema, stringListOfUrlsSchemaRequired, zodCohercedNumber } from '../../lib/zodUtils'
const serviceSchemaBase = z.object({
id: z.number().int().positive(),
@@ -19,11 +15,10 @@ const serviceSchemaBase = z.object({
.string()
.regex(/^[a-z0-9-]+$/, 'Allowed characters: lowercase letters, numbers, and hyphens')
.optional(),
- name: z.string().min(1).max(20),
+ name: z.string().min(1).max(40),
description: z.string().min(1),
- serviceUrls: stringListOfUrlsSchemaRequired,
+ allServiceUrls: stringListOfUrlsSchemaRequired,
tosUrls: stringListOfUrlsSchemaRequired,
- onionUrls: stringListOfUrlsSchema,
kycLevel: z.coerce.number().int().min(0).max(4),
attributes: z.array(z.coerce.number().int().positive()),
categories: z.array(z.coerce.number().int().positive()).min(1),
@@ -85,13 +80,20 @@ export const adminServiceActions = {
? await saveFileLocally(input.imageFile, input.imageFile.name)
: undefined
+ const {
+ web: serviceUrls,
+ onion: onionUrls,
+ i2p: i2pUrls,
+ } = separateServiceUrlsByType(input.allServiceUrls)
+
const service = await prisma.service.create({
data: {
name: input.name,
description: input.description,
- serviceUrls: input.serviceUrls,
+ serviceUrls,
tosUrls: input.tosUrls,
- onionUrls: input.onionUrls,
+ onionUrls,
+ i2pUrls,
kycLevel: input.kycLevel,
verificationStatus: input.verificationStatus,
verificationSummary: input.verificationSummary,
@@ -187,14 +189,21 @@ export const adminServiceActions = {
const attributesToAdd = input.attributes.filter((aId) => !existingAttributeIds.includes(aId))
const attributesToRemove = existingAttributeIds.filter((aId) => !input.attributes.includes(aId))
+ const {
+ web: serviceUrls,
+ onion: onionUrls,
+ i2p: i2pUrls,
+ } = separateServiceUrlsByType(input.allServiceUrls)
+
const service = await prisma.service.update({
where: { id: input.id },
data: {
name: input.name,
description: input.description,
- serviceUrls: input.serviceUrls,
+ serviceUrls,
tosUrls: input.tosUrls,
- onionUrls: input.onionUrls,
+ onionUrls,
+ i2pUrls,
kycLevel: input.kycLevel,
verificationStatus: input.verificationStatus,
verificationSummary: input.verificationSummary,
diff --git a/web/src/actions/serviceSuggestion.ts b/web/src/actions/serviceSuggestion.ts
index 8baa39b..d3833fb 100644
--- a/web/src/actions/serviceSuggestion.ts
+++ b/web/src/actions/serviceSuggestion.ts
@@ -14,12 +14,8 @@ import { defineProtectedAction } from '../lib/defineProtectedAction'
import { saveFileLocally } from '../lib/fileStorage'
import { handleHoneypotTrap } from '../lib/honeypot'
import { prisma } from '../lib/prisma'
-import {
- imageFileSchemaRequired,
- stringListOfUrlsSchema,
- stringListOfUrlsSchemaRequired,
- zodCohercedNumber,
-} from '../lib/zodUtils'
+import { separateServiceUrlsByType } from '../lib/urls'
+import { imageFileSchemaRequired, stringListOfUrlsSchemaRequired, zodCohercedNumber } from '../lib/zodUtils'
import type { Prisma } from '@prisma/client'
@@ -161,9 +157,8 @@ export const serviceSuggestionActions = {
{ message: 'Slug must be unique, try a different one' }
),
description: z.string().min(1).max(SUGGESTION_DESCRIPTION_MAX_LENGTH),
- serviceUrls: stringListOfUrlsSchemaRequired,
+ allServiceUrls: stringListOfUrlsSchemaRequired,
tosUrls: stringListOfUrlsSchemaRequired,
- onionUrls: stringListOfUrlsSchema,
kycLevel: zodCohercedNumber(z.coerce.number().int().min(0).max(4)),
attributes: z.array(z.coerce.number().int().positive()),
categories: z.array(z.coerce.number().int().positive()).min(1),
@@ -210,6 +205,12 @@ export const serviceSuggestionActions = {
const imageUrl = await saveFileLocally(input.imageFile, input.imageFile.name)
+ const {
+ web: serviceUrls,
+ onion: onionUrls,
+ i2p: i2pUrls,
+ } = separateServiceUrlsByType(input.allServiceUrls)
+
const { serviceSuggestion, service } = await prisma.$transaction(async (tx) => {
const serviceSelect = {
id: true,
@@ -221,9 +222,10 @@ export const serviceSuggestionActions = {
name: input.name,
slug: input.slug,
description: input.description,
- serviceUrls: input.serviceUrls,
+ serviceUrls,
tosUrls: input.tosUrls,
- onionUrls: input.onionUrls,
+ onionUrls,
+ i2pUrls,
kycLevel: input.kycLevel,
acceptedCurrencies: input.acceptedCurrencies,
imageUrl,
diff --git a/web/src/components/ServicesSearchResults.astro b/web/src/components/ServicesSearchResults.astro
index 34f59dc..1303df4 100644
--- a/web/src/components/ServicesSearchResults.astro
+++ b/web/src/components/ServicesSearchResults.astro
@@ -21,7 +21,6 @@ type Props = HTMLAttributes<'div'> & {
pageSize: number
sortSeed?: string
filters: ServicesFiltersObject
- includeScams: boolean
countCommunityOnly: number | null
inlineIcons?: boolean
}
@@ -35,15 +34,12 @@ const {
sortSeed,
class: className,
filters,
- includeScams,
countCommunityOnly,
inlineIcons,
...divProps
} = Astro.props
-const hasScams =
- // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
- filters.verification.includes('VERIFICATION_FAILED') || includeScams
+const hasScams = filters.verification.includes('VERIFICATION_FAILED')
const hasSomeScam = !!services?.some((service) => service.verificationStatus.includes('VERIFICATION_FAILED'))
const hasCommunityContributed = filters.verification.includes('COMMUNITY_CONTRIBUTED')
diff --git a/web/src/lib/urls.ts b/web/src/lib/urls.ts
index a35b3c6..ba40ee7 100644
--- a/web/src/lib/urls.ts
+++ b/web/src/lib/urls.ts
@@ -113,3 +113,28 @@ export function urlDomain(url: URL | string) {
}
return url.origin
}
+
+export function separateServiceUrlsByType(allServiceUrls: string[]) {
+ const result: {
+ web: string[]
+ onion: string[]
+ i2p: string[]
+ } = {
+ web: [],
+ onion: [],
+ i2p: [],
+ }
+
+ for (const url of allServiceUrls) {
+ const parsedUrl = new URL(url)
+ if (parsedUrl.origin.endsWith('.onion')) {
+ result.onion.push(url)
+ } else if (parsedUrl.origin.endsWith('.b32.i2p')) {
+ result.i2p.push(url)
+ } else {
+ result.web.push(url)
+ }
+ }
+
+ return result
+}
diff --git a/web/src/lib/zodUtils.ts b/web/src/lib/zodUtils.ts
index e1d1b95..b2f5a3f 100644
--- a/web/src/lib/zodUtils.ts
+++ b/web/src/lib/zodUtils.ts
@@ -19,7 +19,7 @@ export const zodUrlOptionalProtocol = z.preprocess(
const cleanInput = input.trim().replace(/\/$/, '')
return !/^\w+:\/\//i.test(cleanInput) ? `https://${cleanInput}` : cleanInput
},
- z.string().refine((value) => /^(https?):\/\/(?=.*\.[a-z]{2,})[^\s$.?#].[^\s]*$/i.test(value), {
+ z.string().refine((value) => /^(https?):\/\/(?=.*\.[a-z0-9]{2,})[^\s$.?#].[^\s]*$/i.test(value), {
message: 'Invalid URL',
})
)
diff --git a/web/src/pages/admin/services/[slug]/edit.astro b/web/src/pages/admin/services/[slug]/edit.astro
index abce1aa..889b64c 100644
--- a/web/src/pages/admin/services/[slug]/edit.astro
+++ b/web/src/pages/admin/services/[slug]/edit.astro
@@ -233,16 +233,30 @@ if (!service) return Astro.rewrite('/404')
enctype="multipart/form-data"
>
-
{inputErrors.serviceUrls.join(', ')}
+ inputErrors.allServiceUrls && ( +{inputErrors.allServiceUrls.join(', ')}
) }{inputErrors.onionUrls.join(', ')}
- ) - } -