Release 202505311001
This commit is contained in:
@@ -46,6 +46,9 @@ export const apiServiceActions = {
|
||||
|
||||
const service = await prisma.service.findFirst({
|
||||
where: {
|
||||
listedAt: { lte: new Date() },
|
||||
serviceVisibility: { in: ['PUBLIC', 'ARCHIVED', 'UNLISTED'] },
|
||||
|
||||
OR: [
|
||||
...(input.id ? ([{ id: input.id }] satisfies Prisma.ServiceWhereInput[]) : []),
|
||||
...(input.slug ? ([{ slug: input.slug }] satisfies Prisma.ServiceWhereInput[]) : []),
|
||||
@@ -76,10 +79,20 @@ export const apiServiceActions = {
|
||||
i2pUrls: true,
|
||||
tosUrls: true,
|
||||
referral: true,
|
||||
listedAt: true,
|
||||
verifiedAt: true,
|
||||
serviceVisibility: true,
|
||||
},
|
||||
})
|
||||
|
||||
if (!service) {
|
||||
if (
|
||||
!service ||
|
||||
(service.serviceVisibility !== 'PUBLIC' &&
|
||||
service.serviceVisibility !== 'ARCHIVED' &&
|
||||
service.serviceVisibility !== 'UNLISTED') ||
|
||||
!service.listedAt ||
|
||||
service.listedAt > new Date()
|
||||
) {
|
||||
throw new ActionError({
|
||||
code: 'NOT_FOUND',
|
||||
message: 'Service not found',
|
||||
@@ -91,6 +104,7 @@ export const apiServiceActions = {
|
||||
slug: service.slug,
|
||||
name: service.name,
|
||||
description: service.description,
|
||||
serviceVisibility: service.serviceVisibility,
|
||||
verificationStatus: service.verificationStatus,
|
||||
verificationStatusInfo: pick(getVerificationStatusInfo(service.verificationStatus), [
|
||||
'value',
|
||||
@@ -99,9 +113,11 @@ export const apiServiceActions = {
|
||||
'labelShort',
|
||||
'description',
|
||||
]),
|
||||
verifiedAt: service.verifiedAt,
|
||||
kycLevel: service.kycLevel,
|
||||
kycLevelInfo: pick(getKycLevelInfo(service.kycLevel.toString()), ['value', 'name', 'description']),
|
||||
categories: service.categories,
|
||||
listedAt: service.listedAt,
|
||||
serviceUrls: [...service.serviceUrls, ...service.onionUrls, ...service.i2pUrls].map(
|
||||
(url) => url + (service.referral ?? '')
|
||||
),
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
import {
|
||||
Currency,
|
||||
ServiceSuggestionStatus,
|
||||
ServiceSuggestionType,
|
||||
ServiceVisibility,
|
||||
VerificationStatus,
|
||||
} from '@prisma/client'
|
||||
import { Currency } from '@prisma/client'
|
||||
import { z } from 'astro/zod'
|
||||
import { ActionError } from 'astro:actions'
|
||||
import { formatDistanceStrict } from 'date-fns'
|
||||
@@ -118,9 +112,9 @@ export const serviceSuggestionActions = {
|
||||
|
||||
const serviceSuggestion = await prisma.serviceSuggestion.create({
|
||||
data: {
|
||||
type: ServiceSuggestionType.EDIT_SERVICE,
|
||||
type: 'EDIT_SERVICE',
|
||||
notes: combinedNotes,
|
||||
status: ServiceSuggestionStatus.PENDING,
|
||||
status: 'PENDING',
|
||||
userId: context.locals.user.id,
|
||||
serviceId: service.id,
|
||||
},
|
||||
@@ -229,12 +223,12 @@ export const serviceSuggestionActions = {
|
||||
kycLevel: input.kycLevel,
|
||||
acceptedCurrencies: input.acceptedCurrencies,
|
||||
imageUrl,
|
||||
verificationStatus: VerificationStatus.COMMUNITY_CONTRIBUTED,
|
||||
verificationStatus: 'COMMUNITY_CONTRIBUTED',
|
||||
overallScore: 0,
|
||||
privacyScore: 0,
|
||||
trustScore: 0,
|
||||
listedAt: new Date(),
|
||||
serviceVisibility: ServiceVisibility.UNLISTED,
|
||||
serviceVisibility: 'UNLISTED',
|
||||
categories: {
|
||||
connect: input.categories.map((id) => ({ id })),
|
||||
},
|
||||
@@ -250,8 +244,8 @@ export const serviceSuggestionActions = {
|
||||
const serviceSuggestion = await tx.serviceSuggestion.create({
|
||||
data: {
|
||||
notes: input.notes,
|
||||
type: ServiceSuggestionType.CREATE_SERVICE,
|
||||
status: ServiceSuggestionStatus.PENDING,
|
||||
type: 'CREATE_SERVICE',
|
||||
status: 'PENDING',
|
||||
userId: context.locals.user.id,
|
||||
serviceId: service.id,
|
||||
},
|
||||
|
||||
@@ -15,17 +15,31 @@ const links = [
|
||||
icon: 'ri:git-repository-line',
|
||||
external: true,
|
||||
},
|
||||
...(Astro.url.origin !== new URL(ONION_ADDRESS).origin
|
||||
? [
|
||||
{
|
||||
href: ONION_ADDRESS,
|
||||
label: 'Tor',
|
||||
icon: 'onion',
|
||||
external: true,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(Astro.url.origin !== new URL(I2P_ADDRESS).origin
|
||||
? [
|
||||
{
|
||||
href: I2P_ADDRESS,
|
||||
label: 'I2P',
|
||||
icon: 'i2p',
|
||||
external: true,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
href: ONION_ADDRESS,
|
||||
label: 'Tor',
|
||||
icon: 'onion',
|
||||
external: true,
|
||||
},
|
||||
{
|
||||
href: I2P_ADDRESS,
|
||||
label: 'I2P',
|
||||
icon: 'i2p',
|
||||
external: true,
|
||||
href: '/docs/api',
|
||||
label: 'API',
|
||||
icon: 'ri:plug-line',
|
||||
external: false,
|
||||
},
|
||||
{
|
||||
href: '/about',
|
||||
|
||||
@@ -65,7 +65,7 @@ export const {
|
||||
value: 'ARCHIVED',
|
||||
slug: 'archived',
|
||||
label: 'Archived',
|
||||
description: 'No longer operational',
|
||||
description: 'No longer operational.',
|
||||
longDescription:
|
||||
'Archived service, no longer exists or ceased operations. Information may be outdated.',
|
||||
icon: 'ri:archive-line',
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
import { RELEASE_DATE, RELEASE_NUMBER } from 'astro:env/server'
|
||||
|
||||
import TimeFormatted from '../../components/TimeFormatted.astro'
|
||||
import MiniLayout from '../../layouts/MiniLayout.astro'
|
||||
import { timeAgo } from '../../lib/timeAgo'
|
||||
|
||||
const releaseDate =
|
||||
RELEASE_DATE && !isNaN(new Date(RELEASE_DATE).getTime()) ? new Date(RELEASE_DATE) : undefined
|
||||
@@ -37,7 +37,7 @@ const releaseDate =
|
||||
{
|
||||
!!releaseDate && (
|
||||
<p class="text-day-500 mt-2">
|
||||
(<TimeFormatted date={releaseDate} hourPrecision daysUntilDate={Infinity} />)
|
||||
(<time datetime={releaseDate.toISOString()}>{timeAgo.format(releaseDate, 'round')}</time>)
|
||||
</p>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ icon: 'ri:plug-line'
|
||||
import { SOURCE_CODE_URL } from 'astro:env/server'
|
||||
import { kycLevels } from '../../constants/kycLevels'
|
||||
import { verificationStatuses } from '../../constants/verificationStatus'
|
||||
import { serviceVisibilities } from '../../constants/serviceVisibility'
|
||||
|
||||
Access basic service data via our public API.
|
||||
|
||||
@@ -41,6 +42,7 @@ type ServiceResponse = {
|
||||
slug: string
|
||||
name: string
|
||||
description: string
|
||||
serviceVisibility: 'PUBLIC' | 'ARCHIVED' | 'UNLISTED'
|
||||
verificationStatus: 'VERIFICATION_SUCCESS' | 'APPROVED' | 'COMMUNITY_CONTRIBUTED' | 'VERIFICATION_FAILED'
|
||||
verificationStatusInfo: {
|
||||
value: 'VERIFICATION_SUCCESS' | 'APPROVED' | 'COMMUNITY_CONTRIBUTED' | 'VERIFICATION_FAILED'
|
||||
@@ -49,6 +51,7 @@ type ServiceResponse = {
|
||||
labelShort: string
|
||||
description: string
|
||||
}
|
||||
verifiedAt: Date | null
|
||||
kycLevel: 0 | 1 | 2 | 3 | 4
|
||||
kycLevelInfo: {
|
||||
value: 0 | 1 | 2 | 3 | 4
|
||||
@@ -59,13 +62,14 @@ type ServiceResponse = {
|
||||
name: string
|
||||
slug: string
|
||||
}[]
|
||||
listedAt: Date
|
||||
serviceUrls: string[]
|
||||
tosUrls: string[]
|
||||
kycnotmeUrl: `https://kycnot.me/service/${service.slug}`
|
||||
}
|
||||
```
|
||||
|
||||
### KYC Levels
|
||||
#### KYC Levels
|
||||
|
||||
<ul>
|
||||
{kycLevels.map((level) => (
|
||||
@@ -75,7 +79,7 @@ type ServiceResponse = {
|
||||
))}
|
||||
</ul>
|
||||
|
||||
### Verification Status
|
||||
#### Verification Status
|
||||
|
||||
<ul>
|
||||
{verificationStatuses.map((status) => (
|
||||
@@ -85,7 +89,19 @@ type ServiceResponse = {
|
||||
))}
|
||||
</ul>
|
||||
|
||||
### Example Request
|
||||
#### Service Visibility
|
||||
|
||||
<ul>
|
||||
{serviceVisibilities.filter((visibility) => visibility.value === 'PUBLIC' || visibility.value === 'ARCHIVED' || visibility.value === 'UNLISTED').map((visibility) => (
|
||||
<li key={visibility.value}>
|
||||
<strong>{visibility.value}</strong>: {visibility.longDescription}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
### Examples
|
||||
|
||||
#### Request
|
||||
|
||||
```zsh
|
||||
curl -X QUERY https://kycnot.me/api/v1/service/get \
|
||||
@@ -93,12 +109,13 @@ curl -X QUERY https://kycnot.me/api/v1/service/get \
|
||||
-d '{"slug": "my-example-service"}'
|
||||
```
|
||||
|
||||
### Example Response
|
||||
#### Response
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "My Example Service",
|
||||
"description": "This is a description of my example service",
|
||||
"serviceVisibility": "PUBLIC",
|
||||
"verificationStatus": "VERIFICATION_SUCCESS",
|
||||
"verificationStatusInfo": {
|
||||
"value": "VERIFICATION_SUCCESS",
|
||||
@@ -107,6 +124,7 @@ curl -X QUERY https://kycnot.me/api/v1/service/get \
|
||||
"labelShort": "Verified",
|
||||
"description": "Thoroughly tested and verified by the team. But things might change, this is not a guarantee."
|
||||
},
|
||||
"verifiedAt": "2025-01-20T07:12:29.393Z",
|
||||
"kycLevel": 0,
|
||||
"kycLevelInfo": {
|
||||
"value": 0,
|
||||
@@ -119,6 +137,7 @@ curl -X QUERY https://kycnot.me/api/v1/service/get \
|
||||
"slug": "exchange"
|
||||
}
|
||||
],
|
||||
"listedAt": "2025-05-31T19:09:18.043Z",
|
||||
"serviceUrls": [
|
||||
"https://example.com",
|
||||
"http://c9ikae0fdidzh1ufrzp022e5uqfvz6ofxlkycz59cvo6fdxjgx7ekl9e.onion"
|
||||
@@ -128,7 +147,7 @@ curl -X QUERY https://kycnot.me/api/v1/service/get \
|
||||
}
|
||||
```
|
||||
|
||||
### Error Responses
|
||||
#### Error Responses
|
||||
|
||||
**404 Not Found**: Service not found
|
||||
|
||||
|
||||
@@ -218,16 +218,12 @@ const servicesQMatch = filters.q ? await findServicesBySimilarity(filters.q) : n
|
||||
|
||||
const where = {
|
||||
id: servicesQMatch ? { in: servicesQMatch.map(({ id }) => id) } : undefined,
|
||||
listedAt: {
|
||||
lte: new Date(),
|
||||
},
|
||||
listedAt: { lte: new Date() },
|
||||
categories: filters.categories.length ? { some: { slug: { in: filters.categories } } } : undefined,
|
||||
verificationStatus: {
|
||||
in: includeScams ? uniq([...filters.verification, 'VERIFICATION_FAILED'] as const) : filters.verification,
|
||||
},
|
||||
serviceVisibility: {
|
||||
in: ['PUBLIC', 'ARCHIVED'],
|
||||
},
|
||||
serviceVisibility: { in: ['PUBLIC', 'ARCHIVED'] },
|
||||
overallScore: { gte: filters['min-score'] },
|
||||
acceptedCurrencies: filters.currencies.length
|
||||
? filters['currency-mode'] === 'and'
|
||||
@@ -319,9 +315,8 @@ const [categories, [services, totalServices], countCommunityOnly, attributes] =
|
||||
select: {
|
||||
services: {
|
||||
where: {
|
||||
serviceVisibility: {
|
||||
in: ['PUBLIC', 'ARCHIVED'],
|
||||
},
|
||||
serviceVisibility: { in: ['PUBLIC', 'ARCHIVED'] },
|
||||
listedAt: { lte: new Date() },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -94,7 +94,16 @@ const user = await Astro.locals.banners.try('user', async () => {
|
||||
},
|
||||
},
|
||||
},
|
||||
where: { service: { serviceVisibility: 'PUBLIC' } },
|
||||
where: {
|
||||
service: {
|
||||
listedAt: {
|
||||
lte: new Date(),
|
||||
},
|
||||
serviceVisibility: {
|
||||
in: ['PUBLIC', 'ARCHIVED'],
|
||||
},
|
||||
},
|
||||
},
|
||||
orderBy: { createdAt: 'desc' },
|
||||
take: 5,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user