Properly applied code quality tools

main
Štěpán Škorpil 2022-11-03 19:38:01 +01:00
rodzic 8958ab363a
commit 2dda770993
63 zmienionych plików z 5936 dodań i 21460 usunięć

Wyświetl plik

@ -1,38 +0,0 @@
{
"env": {
"browser": true,
"es2021": true
},
"extends": [
"plugin:react/recommended",
"eslint:recommended",
"plugin:@next/next/recommended",
"standard"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": 12,
"sourceType": "module"
},
"plugins": [
"react",
"@typescript-eslint",
"react",
"react-hooks",
"jsx-a11y",
"import",
"@next/next"
],
"rules": {
"no-use-before-define": "off",
"@typescript-eslint/no-use-before-define": ["error"]
},
"settings": {
"react": {
"version": "detect"
}
}
}

Wyświetl plik

@ -1,4 +0,0 @@
{
"printWidth": 100,
"singleQuote": true
}

Wyświetl plik

@ -15,5 +15,8 @@ module.exports = {
permanent: true
}
]
},
typescript: {
tsconfigPath: '../tsconfig.json'
}
}

21099
application/package-lock.json wygenerowano

Plik diff jest za duży Load Diff

Wyświetl plik

@ -16,17 +16,20 @@
"@apollo/client": "^3.6.9",
"@datapunt/matomo-tracker-js": "^0.5.1",
"@elastic/elasticsearch": "^8.2.1",
"@fortawesome/fontawesome-svg-core": "^1.2.36",
"@fortawesome/free-brands-svg-icons": "^5.15.4",
"@fortawesome/free-regular-svg-icons": "^5.15.4",
"@fortawesome/free-solid-svg-icons": "^5.15.4",
"@fortawesome/react-fontawesome": "^0.1.17",
"@hookform/resolvers": "^2.8.5",
"@fortawesome/fontawesome-common-types": "^6.2.0",
"@fortawesome/fontawesome-svg-core": "^6.2.0",
"@fortawesome/free-brands-svg-icons": "^6.2.0",
"@fortawesome/free-regular-svg-icons": "^6.2.0",
"@fortawesome/free-solid-svg-icons": "^6.2.0",
"@fortawesome/react-fontawesome": "^0.2.0",
"@hookform/resolvers": "^2.9.10",
"@popperjs/core": "^2.11.6",
"@svgr/webpack": "^6.2.1",
"apollo-server-micro": "^3.10.1",
"axios": "^0.21.1",
"bootstrap": "^5.1.3",
"graphql": "^16.5.0",
"micro": "^9.4.1",
"micro-cors": "^0.1.1",
"next": "^12.2.5",
"nexus": "^1.3.0",
@ -40,25 +43,48 @@
"zod": "^3.11.6"
},
"devDependencies": {
"@next/eslint-plugin-next": "^12.0.7",
"@types/jest": "^27.0.2",
"@next/eslint-plugin-next": "^13.0.0",
"@types/jest": "^29.2.0",
"@types/micro-cors": "^0.1.2",
"@types/node": "^18.7.18",
"@types/npmlog": "^4.1.3",
"@types/react": "^17.0.14",
"@typescript-eslint/eslint-plugin": "^5.4.0",
"@typescript-eslint/parser": "^5.4.0",
"eslint": "^7.32.0",
"eslint-config-standard": "^16.0.3",
"eslint-plugin-import": "^2.25.3",
"eslint-plugin-jsx-a11y": "^6.5.1",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^5.1.1",
"eslint-plugin-react": "^7.27.1",
"eslint-plugin-react-hooks": "^4.3.0",
"jest": "^27.3.0",
"standard": "*",
"ts-jest": "^27.0.7",
"@typescript-eslint/eslint-plugin": "^5.37.0",
"@typescript-eslint/parser": "^5.37.0",
"eslint": "^8.0.1",
"eslint-config-standard-react": "^12.0.0",
"eslint-config-standard-with-typescript": "^23.0.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-n": "^15.2.5",
"eslint-plugin-promise": "^6.0.1",
"eslint-plugin-react": "^7.31.8",
"jest": "^29.2.2",
"ts-jest": "^29.0.3",
"typescript": "^4.3.5"
},
"eslintConfig": {
"env": {
"browser": true,
"es2021": true
},
"extends": [
"standard-with-typescript",
"standard-react"
],
"parserOptions": {
"project": [
"tsconfig.json"
]
},
"rules": {
"@typescript-eslint/no-misused-promises": [
"error",
{
"checksVoidReturn": {
"attributes": false
}
}
]
}
}
}

Wyświetl plik

@ -1,14 +1,14 @@
import React from 'react'
import FallbackImage from './FallbackImage'
const Avatar:React.FC<{url:string|null|undefined}> = ({ url }) => {
const Avatar: React.FC<{ url: string | null | undefined }> = ({ url }) => {
return (
<FallbackImage
className={'avatar'}
src={url}
fallbackSrc={'/avatar.svg'}
alt={'Avatar'}
/>
<FallbackImage
className={'avatar'}
src={url ?? undefined}
fallbackSrc={'/avatar.svg'}
alt={'Avatar'}
/>
)
}
export default Avatar

Wyświetl plik

@ -1,14 +1,20 @@
import React, { ImgHTMLAttributes, useEffect, useState } from 'react'
export default function FallbackImage ({ fallbackSrc, src, alt, ...props }: ImgHTMLAttributes<HTMLImageElement>&{fallbackSrc?:string}) {
import React, { ImgHTMLAttributes, ReactElement, useEffect, useState } from 'react'
export default function FallbackImage ({
fallbackSrc,
src,
alt,
...props
}: ImgHTMLAttributes<HTMLImageElement> & { fallbackSrc?: string }): ReactElement {
const [showFallback, setShowFallback] = useState<boolean>(false)
useEffect(() => {
setShowFallback(!src)
setShowFallback(src === undefined || src === null || src === '')
}, [src])
const handleError = (event): void => {
if (props.onError) {
if (props.onError != null) {
props.onError(event)
}
if (!fallbackSrc) {
if (fallbackSrc === undefined || fallbackSrc === '') {
return
}
setShowFallback(true)

Wyświetl plik

@ -1,4 +1,4 @@
import React, { useEffect } from 'react'
import React, { ReactElement, useEffect } from 'react'
import striptags from 'striptags'
import Avatar from './Avatar'
import SoftwareBadge from './badges/SoftwareBadge'
@ -14,16 +14,16 @@ import { FeedResultItem } from '../graphql/client/queries/ListFeedsQuery'
const FeedResult = ({
feed
}:{ feed: FeedResultItem }) => {
}: { feed: FeedResultItem }): ReactElement => {
const fallbackEmojiImage = '/emoji.svg'
const handleEmojiImageError = (event) => {
const handleEmojiImageError = (event): void => {
event.target.src = fallbackEmojiImage
}
useEffect(() => {
document.querySelectorAll('.with-emoji img').forEach(element => {
if (element.attributes['data-error-handler']) {
if (element.attributes['data-error-handler'] === 'attached') {
return
}
element.addEventListener('error', handleEmojiImageError)

Wyświetl plik

@ -1,10 +1,10 @@
import React from 'react'
import React, { ReactElement } from 'react'
import FeedResult from './FeedResult'
import { FeedResultItem } from '../graphql/client/queries/ListFeedsQuery'
const FeedResults = ({
feeds
}:{ feeds: FeedResultItem[] }) => {
}: { feeds: FeedResultItem[] }): ReactElement => {
if (feeds.length === 0) {
return (
<>

Wyświetl plik

@ -1,6 +1,6 @@
import React from 'react'
const Footer:React.FC = () => {
const Footer: React.FC = () => {
return (
<footer className={'text-center mt-5'}>
©{(new Date()).getFullYear()} <a href={'https://skorpil.cz'}>Štěpán Škorpil</a>

Wyświetl plik

@ -1,6 +1,6 @@
import React from 'react'
export function getFlagEmoji (countryCode:string):string {
export function getFlagEmoji (countryCode: string): string {
const codePoints = countryCode
.toUpperCase()
.split('')
@ -8,18 +8,28 @@ export function getFlagEmoji (countryCode:string):string {
return String.fromCodePoint(...codePoints)
}
export interface GeoParams{
countryCode?: string,
countryName?:string,
city?: string
export interface GeoParams {
countryCode?: string
countryName?: string
city?: string
}
export default function Geo ({ countryCode, countryName, city }:GeoParams):React.ReactElement|null {
if (!countryCode && !city) {
export default function Geo ({ countryCode, countryName, city }: GeoParams): React.ReactElement | null {
const alignedCountryCode = countryCode ?? ''
const alignedCity = city ?? ''
if (alignedCountryCode === '' && alignedCity === '') {
return null
}
return <div className={'geo'}>
{city ? <span className={'city'}>{city}</span> : ''}
{countryCode ? <span className={'country'} title={`${countryName ?? countryCode}`}>{getFlagEmoji(countryCode)}</span> : ''}
{
alignedCity !== ''
? <span className={'city'}>{alignedCity}</span>
: ''
}
{
alignedCountryCode !== ''
? <span className={'country'} title={`${countryName ?? alignedCountryCode}`}>{getFlagEmoji(alignedCountryCode)}</span>
: ''
}
</div>
}

Wyświetl plik

@ -17,10 +17,10 @@ const Loader: React.FC<{ children: ReactNode, loading: boolean, hideContent?: bo
</div>
)
if (table) {
if (table !== undefined || table !== 0) {
return (
<>
{showTop && loading
{(showTop ?? false) && loading
? (
<tbody>
<tr className={className}>
@ -31,8 +31,8 @@ const Loader: React.FC<{ children: ReactNode, loading: boolean, hideContent?: bo
</tbody>
)
: ''}
{hideContent && loading ? '' : children}
{showBottom && loading
{(hideContent ?? false) && loading ? '' : children}
{(showBottom ?? false) && loading
? (
<tbody>
<tr className={className}>
@ -50,9 +50,9 @@ const Loader: React.FC<{ children: ReactNode, loading: boolean, hideContent?: bo
}
return (
<>
{showTop && loading ? spinner : ''}
{hideContent && loading ? '' : children}
{showBottom && loading ? spinner : ''}
{(showTop ?? false) && loading ? spinner : ''}
{(hideContent ?? false) && loading ? '' : children}
{(showBottom ?? false) && loading ? spinner : ''}
</>
)
}

Wyświetl plik

@ -3,7 +3,7 @@ import NavItem from './NavItem'
import { faUser, faServer, faChartPie } from '@fortawesome/free-solid-svg-icons'
import FallbackImage from './FallbackImage'
const NavBar:React.FC = () => {
const NavBar: React.FC = () => {
const [showMenu, setShowMenu] = useState<boolean>(false)
return (
<nav className="navbar navbar-expand-lg navbar-dark bg-dark mb-4">

Wyświetl plik

@ -4,7 +4,7 @@ import { useRouter } from 'next/router'
import { IconProp } from '@fortawesome/fontawesome-svg-core'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
const NavItem:FC<{path:string, label:string, icon:IconProp}> = ({ path, label, icon }) => {
const NavItem: FC<{ path: string, label: string, icon: IconProp }> = ({ path, label, icon }) => {
const router = useRouter()
const active = router.pathname === path
return (

Wyświetl plik

@ -2,8 +2,8 @@ import React from 'react'
import Avatar from './Avatar'
import { ParentFeedItem } from '../graphql/client/queries/ListFeedsQuery'
const ParentFeed: React.FC<{feed:ParentFeedItem|null}> = ({ feed }) => {
if (!feed) {
const ParentFeed: React.FC<{ feed: ParentFeedItem | null }> = ({ feed }) => {
if (feed == null) {
return (<></>)
}
return (

Wyświetl plik

@ -1,6 +1,6 @@
import React from 'react'
const ProgressBar: React.FC<{ percents: number, way?: 'left' | 'right' | 'top' | 'bottom', color?:string }> = ({ percents, way, color }) => {
const ProgressBar: React.FC<{ percents: number, way?: 'left' | 'right' | 'top' | 'bottom', color?: string }> = ({ percents, way, color }) => {
way = way ?? 'right'
percents = Math.round(percents)
color = color ?? 'var(--accent-color)'

Wyświetl plik

@ -4,8 +4,8 @@ import { faSortUp, faSortDown } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
const SortToggle: React.FC<{
onToggle:(StatsRequestSortBy)=>void,
field:string,
onToggle: (StatsRequestSortBy) => void
field: string
sort: Sort
}> = ({ onToggle, field, sort, children }) => {
return (

Wyświetl plik

@ -2,12 +2,12 @@ import React from 'react'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { IconProp } from '@fortawesome/fontawesome-svg-core'
const Badge:React.FC<{ faIcon:IconProp, label:string, value:string|number|null, className?:string, showUnknown?:boolean }> = ({ faIcon, label, value, className, showUnknown }) => {
const Badge: React.FC<{ faIcon: IconProp, label: string, value: string | number | null, className?: string, showUnknown?: boolean }> = ({ faIcon, label, value, className, showUnknown }) => {
if (value === null && showUnknown !== true) {
return (<></>)
}
return (
<div className={'badge bg-secondary ' + className} title={label}>
<div className={`badge bg-secondary ${className ?? ''}`} title={label}>
<FontAwesomeIcon icon={faIcon} className={'margin-right'}/>
<span className="visually-hidden">{label}:</span>
<span className={'value'}>{value === null ? '?' : value}</span>

Wyświetl plik

@ -2,7 +2,7 @@ import React from 'react'
import { faRobot } from '@fortawesome/free-solid-svg-icons'
import Badge from './Badge'
const BotBadge:React.FC<{ bot: boolean | null}> = ({ bot }) => {
const BotBadge: React.FC<{ bot: boolean | null }> = ({ bot }) => {
return (
<Badge faIcon={faRobot}
label={'Bot'}

Wyświetl plik

@ -2,7 +2,7 @@ import React from 'react'
import { faCalendarPlus } from '@fortawesome/free-solid-svg-icons'
import Badge from './Badge'
const CreatedAtBadge:React.FC<{ createdAt: string | null }> = ({ createdAt }) => {
const CreatedAtBadge: React.FC<{ createdAt: string | null }> = ({ createdAt }) => {
return (
<Badge faIcon={faCalendarPlus}
label={'Created at'}

Wyświetl plik

@ -2,7 +2,7 @@ import React from 'react'
import { faRss, faUser } from '@fortawesome/free-solid-svg-icons'
import Badge from './Badge'
const FeedTypeBadge:React.FC<{ type: 'account' | 'channel' }> = ({ type }) => {
const FeedTypeBadge: React.FC<{ type: 'account' | 'channel' }> = ({ type }) => {
return (
<Badge faIcon={type === 'channel' ? faRss : faUser}
label={'Feed type'}

Wyświetl plik

@ -2,7 +2,7 @@ import { faUserFriends } from '@fortawesome/free-solid-svg-icons'
import React from 'react'
import Badge from './Badge'
const FollowersBadge:React.FC<{ followers: number|null}> = ({ followers }) => {
const FollowersBadge: React.FC<{ followers: number | null }> = ({ followers }) => {
return (
<Badge faIcon={faUserFriends}
label={'Followers'}

Wyświetl plik

@ -2,7 +2,7 @@ import { faEye } from '@fortawesome/free-solid-svg-icons'
import React from 'react'
import Badge from './Badge'
const FollowingBadge:React.FC<{ following: number|null}> = ({ following }) => {
const FollowingBadge: React.FC<{ following: number | null }> = ({ following }) => {
return (
<Badge faIcon={faEye}
label={'Following'}

Wyświetl plik

@ -2,7 +2,7 @@ import React from 'react'
import Badge from './Badge'
import { faCalendarCheck } from '@fortawesome/free-solid-svg-icons'
const LastPostAtBadge:React.FC<{ lastStatusAt: string | null }> = ({ lastStatusAt }) => {
const LastPostAtBadge: React.FC<{ lastStatusAt: string | null }> = ({ lastStatusAt }) => {
return (
<Badge faIcon={faCalendarCheck}
label={'Last status at'}

Wyświetl plik

@ -8,8 +8,8 @@ const SoftwareBadge: React.FC<{ softwareName: string | null }> = ({ softwareName
<FallbackImage className={'icon'}
src={softwareName !== null ? `/software/${softwareName}.svg` : fallbackImage}
fallbackSrc={fallbackImage}
alt={softwareName}
title={softwareName}
alt={softwareName ?? undefined}
title={softwareName ?? undefined}
/>
<span className={'value'}>{softwareName}</span>
</div>)

Wyświetl plik

@ -2,7 +2,7 @@ import React from 'react'
import { faCommentAlt } from '@fortawesome/free-solid-svg-icons'
import Badge from './Badge'
const StatusesCountBadge:React.FC<{ statusesCount: number | null }> = ({ statusesCount }) => {
const StatusesCountBadge: React.FC<{ statusesCount: number | null }> = ({ statusesCount }) => {
return (
<Badge faIcon={faCommentAlt}
label={'Status count'}

Wyświetl plik

@ -1,6 +1,6 @@
import { ApolloClient, InMemoryCache } from '@apollo/client'
import { ApolloClient, InMemoryCache, NormalizedCacheObject } from '@apollo/client'
export default function createGraphqlClient () {
export default function createGraphqlClient (): ApolloClient<NormalizedCacheObject> {
return new ApolloClient({
uri: '/api/graphql',
cache: new InMemoryCache()

Wyświetl plik

@ -57,56 +57,57 @@ export const ListFeedsQuery = gql`
}
`
export type ParentFeedItem = {
id: string,
avatar: string,
displayName: string
export interface ParentFeedItem {
id: string
avatar: string
displayName: string
name: string
domain: string
url: string
}
export interface FeedResultItem {
id: string
avatar: string
displayName: string
foundAt: string
bot: boolean
createdAt: string
description: string
followersCount: number
followingCount: number
lastStatusAt: string
locked: boolean
name: string
refreshedAt: string
statusesCount: number
type: 'account' | 'channel'
url: string
fields: Array<{
name: string
value: string
}>
node: {
domain: string
url:string
foundAt: string
geoip: {
// eslint-disable-next-line camelcase
city_name: string
// eslint-disable-next-line camelcase
country_iso_code: string
}
halfYearActiveUserCount: number
id: string
monthActiveUserCount: number
name: string
openRegistrations: boolean
refreshAttemptedAt: string
refreshedAt: string
softwareName: string
}
parent: ParentFeedItem | null
}
export type FeedResultItem = {
id: string,
avatar: string,
displayName: string,
foundAt: string,
bot: boolean,
createdAt: string,
description: string,
followersCount: number,
followingCount: number,
lastStatusAt: string,
locked: boolean,
name: string,
refreshedAt: string,
statusesCount: number,
type: 'account' | 'channel'
url: string,
fields: {
name: string, value: string
}[],
node: {
domain: string,
foundAt: string,
geoip: {
// eslint-disable-next-line camelcase
city_name: string,
// eslint-disable-next-line camelcase
country_iso_code: string,
},
halfYearActiveUserCount: number,
id: string,
monthActiveUserCount: number,
name: string,
openRegistrations: boolean,
refreshAttemptedAt: string,
refreshedAt: string,
softwareName: string
},
parent: ParentFeedItem|null
}
export type ListFeedsResult = {
listFeeds: List<FeedResultItem>
export interface ListFeedsResult {
listFeeds: List<FeedResultItem>
}

Wyświetl plik

@ -32,30 +32,30 @@ export const ListNodesQuery = gql`
}
`
export type NodeResultItem = {
domain: string,
foundAt: string,
geoip: {
// eslint-disable-next-line camelcase
city_name: string,
// eslint-disable-next-line camelcase
country_iso_code: string,
},
halfYearActiveUserCount: number,
id: string,
monthActiveUserCount: number,
name: string,
openRegistrations: boolean,
refreshAttemptedAt: string,
refreshedAt: string,
softwareName: string,
softwareVersion: string,
standardizedSoftwareVersion:string,
totalUserCount: number,
statusesCount: number,
accountFeedCount: number
export interface NodeResultItem {
domain: string
foundAt: string
geoip: {
// eslint-disable-next-line camelcase
city_name: string
// eslint-disable-next-line camelcase
country_iso_code: string
}
halfYearActiveUserCount: number
id: string
monthActiveUserCount: number
name: string
openRegistrations: boolean
refreshAttemptedAt: string
refreshedAt: string
softwareName: string
softwareVersion: string
standardizedSoftwareVersion: string
totalUserCount: number
statusesCount: number
accountFeedCount: number
}
export type ListNodesResult = {
listNodes: List<NodeResultItem>;
export interface ListNodesResult {
listNodes: List<NodeResultItem>
}

Wyświetl plik

@ -14,13 +14,13 @@ export const ListStatsQuery = gql`
}
`
export type StatsResultItem = {
softwareName: string,
nodeCount: number,
accountFeedCount: number,
channelFeedCount: number
export interface StatsResultItem {
softwareName: string
nodeCount: number
accountFeedCount: number
channelFeedCount: number
}
export type ListStatsResult = {
listStats: List<StatsResultItem>;
export interface ListStatsResult {
listStats: List<StatsResultItem>
}

Wyświetl plik

@ -1,6 +1,6 @@
import { PagingType } from '../../server/schema/types'
export type List<TItem> = {
paging: PagingType,
items: TItem[]
export interface List<TItem> {
paging: PagingType
items: TItem[]
}

Wyświetl plik

@ -1,7 +1,7 @@
import { FeedQueryInputType } from '../types/FeedQueryInput'
import { PagingInputType } from '../types/PagingInput'
export type ListFeedsVariables = {
paging: PagingInputType;
query: FeedQueryInputType
export interface ListFeedsVariables {
paging: PagingInputType
query: FeedQueryInputType
}

Wyświetl plik

@ -1,7 +1,7 @@
import { PagingInputType } from '../types/PagingInput'
import { NodeQueryInputType } from '../types/NodeQueryInput'
export type ListNodesVariables = {
paging: PagingInputType;
query: NodeQueryInputType
export interface ListNodesVariables {
paging: PagingInputType
query: NodeQueryInputType
}

Wyświetl plik

@ -1,5 +1,5 @@
import { StatsQueryInputType } from '../types/StatsQueryInput'
export type ListStatsVariables = {
query: StatsQueryInputType
export interface ListStatsVariables {
query: StatsQueryInputType
}

Wyświetl plik

@ -1,6 +1,6 @@
import { z } from 'zod'
export const NodeSortingByValues:readonly [string, ...string[]] = [
export const NodeSortingByValues: readonly [string, ...string[]] = [
'domain',
'softwareName',
'totalUserCount',
@ -14,4 +14,4 @@ export const NodeSortingByValues:readonly [string, ...string[]] = [
export const nodeSortingBySchema = z.enum(NodeSortingByValues)
export type NodeSoringByEnumType = z.infer<typeof nodeSortingBySchema>;
export type NodeSoringByEnumType = z.infer<typeof nodeSortingBySchema>

Wyświetl plik

@ -1,3 +1,3 @@
export type PagingInputType = {
page: number
export interface PagingInputType {
page: number
}

Wyświetl plik

@ -1,13 +1,14 @@
import { z } from 'zod'
export const createSortingInputSchema = (members:z.ZodEnum<[string, ...string[]]>) => {
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export const createSortingInputSchema = (members: z.ZodEnum<[string, ...string[]]>) => {
return z.object({
sortBy: members,
sortWay: z.enum(['asc', 'desc'])
})
}
export type SortingInputType<TMembers> = {
sortBy: TMembers
sortWay: 'asc'|'desc'
export interface SortingInputType<TMembers> {
sortBy: TMembers
sortWay: 'asc' | 'desc'
}

Wyświetl plik

@ -1,6 +1,6 @@
import { z } from 'zod'
export const StatsSortingByValues:readonly [string, ...string[]] = [
export const StatsSortingByValues: readonly [string, ...string[]] = [
'softwareName',
'nodeCount',
'accountFeedCount',
@ -9,4 +9,4 @@ export const StatsSortingByValues:readonly [string, ...string[]] = [
export const statsSortingBySchema = z.enum(StatsSortingByValues)
export type StatsSoringByEnumType = z.infer<typeof statsSortingBySchema>;
export type StatsSoringByEnumType = z.infer<typeof statsSortingBySchema>

Wyświetl plik

@ -1,10 +1,10 @@
import { ElasticClient } from '../../../lib/storage/ElasticClient'
type Context = {
elasticClient: ElasticClient
defaultPaging: {
limit: 20
}
interface Context {
elasticClient: ElasticClient
defaultPaging: {
limit: 20
}
}
export default Context

Wyświetl plik

@ -3,7 +3,7 @@ import resolvers from './resolvers'
import schema from './schema'
import { createContext } from './context'
export default function createGraphqlServer () {
export default function createGraphqlServer (): ApolloServer {
return new ApolloServer({
schema,
resolvers,

Wyświetl plik

@ -3,7 +3,6 @@ import { join } from 'path'
import * as types from './types'
import * as queries from './queries'
// eslint-disable-next-line no-unused-vars
import * as elastic from './sources/elastic'
const schema = makeSchema({
types: {

Wyświetl plik

@ -21,7 +21,7 @@ export const listNodes = extendType({
default: { default: '', sortBy: 'refreshedAt', sortWay: 'desc' }
})
},
resolve: async (event, { paging, query }:ListNodesVariables, { elasticClient, defaultPaging }: Context) => {
resolve: async (event, { paging, query }: ListNodesVariables, { elasticClient, defaultPaging }: Context) => {
console.info('Searching nodes', { paging, query })
const results = await elasticClient.search<Node>({

Wyświetl plik

@ -5,6 +5,7 @@ import { ListStatsVariables } from '../../../common/queries/listStats'
import nodeIndex from '../../../../lib/storage/Definitions/nodeIndex'
import { StatsQueryInputType } from '../../../common/types/StatsQueryInput'
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
const getSort = (query: StatsQueryInputType) => {
switch (query.sortBy) {
case 'nodeCount':
@ -30,7 +31,7 @@ export const listStats = extendType({
default: { sortBy: 'nodeCount', sortWay: 'desc' }
})
},
resolve: async (event, { query }:ListStatsVariables, { elasticClient }: Context) => {
resolve: async (event, { query }: ListStatsVariables, { elasticClient }: Context) => {
console.info('Searching stats', { query })
const results = await elasticClient.search({
@ -59,7 +60,7 @@ export const listStats = extendType({
sort: {
bucket_sort: {
sort: [
// @ts-ignore
// @ts-expect-error
getSort(query)
]
}
@ -68,16 +69,16 @@ export const listStats = extendType({
}
}
})
type Aggregation = {
buckets:{
key:string,
interface Aggregation {
buckets: Array<{
key: string
// eslint-disable-next-line camelcase
doc_count:number
accountFeedCount: {value:number}
channelFeedCount: {value:number}
}[]
doc_count: number
accountFeedCount: { value: number }
channelFeedCount: { value: number }
}>
}
const software = results.aggregations.software as Aggregation
const software = results?.aggregations?.software as Aggregation
return {
items: software.buckets.map(bucket => {
return {

Wyświetl plik

@ -1,4 +1,4 @@
export default function getFlagEmoji (countryCode:string):string {
export default function getFlagEmoji (countryCode: string): string {
const codePoints = countryCode
.toUpperCase()
.split('')

Wyświetl plik

@ -1,10 +1,10 @@
import { UserOptions } from '@datapunt/matomo-tracker-js/es/types'
import MatomoTracker from '@datapunt/matomo-tracker-js'
let matomo:MatomoTracker|undefined
let matomo: MatomoTracker | undefined
const getMatomo = (config:UserOptions):MatomoTracker => {
if (!matomo) {
const getMatomo = (config: UserOptions): MatomoTracker => {
if (matomo == null) {
console.info('Starting Matomo', config)
matomo = new MatomoTracker(config)
}

Wyświetl plik

@ -1,3 +1,3 @@
export default function getNodeId (name:string, domain:string):string {
export default function getNodeId (name: string, domain: string): string {
return `${name}@${domain}`
}

Wyświetl plik

@ -1,10 +1,12 @@
export const matomoConfig = {
urlBase: typeof process.env.MATOMO_URL === 'string' && process.env.MATOMO_URL !== ''
? process.env.MATOMO_URL
: 'https://domain.tld',
siteId: parseInt(typeof process.env.MATOMO_SITE_ID === 'string' && process.env.MATOMO_SITE_ID !== ''
? process.env.MATOMO_SITE_ID
: '1'
urlBase:
process.env.MATOMO_URL !== undefined && process.env.MATOMO_URL !== ''
? process.env.MATOMO_URL
: 'https://domain.tld',
siteId: parseInt(
process.env.MATOMO_SITE_ID !== undefined && process.env.MATOMO_SITE_ID !== ''
? process.env.MATOMO_SITE_ID
: '1'
),
disabled: !process.env.MATOMO_URL || !process.env.MATOMO_SITE_ID
disabled: (process.env.MATOMO_URL ?? '') === '' || (process.env.MATOMO_SITE_ID ?? '') === ''
}

Wyświetl plik

@ -1,4 +1,4 @@
export default function prepareSimpleQuery (search:string):string {
export default function prepareSimpleQuery (search: string): string {
const tokens = search.split(/\s+/)
const searchContainsWildcard = tokens.filter(token => token.length > 0 && token.slice(-1) === '*').length > 0
return tokens.map(token => searchContainsWildcard ? token : token + '*').join(' ')

Wyświetl plik

@ -1,29 +1,29 @@
import Field from './Field'
interface Feed {
domain: string
foundAt: number,
refreshedAt?: number,
name: string,
fullName: string,
displayName: string,
description: string,
strippedDescription?: string,
followersCount?: number,
followingCount?: number,
statusesCount?: number,
lastStatusAt?: number,
createdAt?: number,
bot?: boolean,
locked: boolean,
url: string,
avatar?: string,
type: 'account' | 'channel',
parentFeedName?: string,
parentFeedDomain?: string
fields: Field[],
extractedEmails: string[],
extractedTags: string[]
domain: string
foundAt: number
refreshedAt?: number
name: string
fullName: string
displayName: string
description: string
strippedDescription?: string
followersCount?: number
followingCount?: number
statusesCount?: number
lastStatusAt?: number
createdAt?: number
bot?: boolean
locked: boolean
url: string
avatar?: string
type: 'account' | 'channel'
parentFeedName?: string
parentFeedDomain?: string
fields: Field[]
extractedEmails: string[]
extractedTags: string[]
}
export default Feed

Wyświetl plik

@ -1,8 +1,8 @@
interface Field {
name: string,
value: string
strippedName?: string
strippedValue?: string
name: string
value: string
strippedName?: string
strippedValue?: string
}
export default Field

Wyświetl plik

@ -1,13 +1,13 @@
interface Geo {
cityName?: string,
continentName?: string,
countryIsoCode?: string,
countryName?: string,
latitude: number
longitude: number
location?: string,
regionIsoCode?: string,
regionName?: string
cityName?: string
continentName?: string
countryIsoCode?: string
countryName?: string
latitude: number
longitude: number
location?: string
regionIsoCode?: string
regionName?: string
}
export default Geo

Wyświetl plik

@ -1,25 +1,25 @@
import Geo from './Geo'
interface Node {
name?:string,
strippedName?:string,
foundAt: number,
refreshAttemptedAt?: number
refreshedAt?: number
openRegistrations?: boolean
domain: string,
serverIps?: string[],
geoip?: Geo[],
softwareName?: string;
softwareVersion?: string
standardizedSoftwareVersion?: string
halfYearActiveUserCount?: number,
monthActiveUserCount?: number,
statusesCount?: number,
totalUserCount?: number,
discoveredByDomain?:string,
accountFeedCount?: number,
channelFeedCount?: number,
name?: string
strippedName?: string
foundAt: number
refreshAttemptedAt?: number
refreshedAt?: number
openRegistrations?: boolean
domain: string
serverIps?: string[]
geoip?: Geo[]
softwareName?: string
softwareVersion?: string
standardizedSoftwareVersion?: string
halfYearActiveUserCount?: number
monthActiveUserCount?: number
statusesCount?: number
totalUserCount?: number
discoveredByDomain?: string
accountFeedCount?: number
channelFeedCount?: number
}
export default Node

Wyświetl plik

@ -6,7 +6,7 @@ const elasticClient = new Client({
},
auth: {
username: process.env.ELASTIC_USER ?? 'elastic',
password: process.env.ELASTIC_PASSWORD
password: process.env.ELASTIC_PASSWORD ?? ''
}
})

Wyświetl plik

@ -1,7 +1,7 @@
import { ZodSchema } from 'zod'
export function preserveUndefined<Source, Target> (cast: (value:Source)=>Target) {
return (value:Source|undefined):Target|undefined => {
export function preserveUndefined<Source, Target> (cast: (value: Source) => Target) {
return (value: Source | undefined): Target | undefined => {
if (value === undefined) {
return undefined
}
@ -9,8 +9,8 @@ export function preserveUndefined<Source, Target> (cast: (value:Source)=>Target)
}
}
export function preserveNull<Source, Target> (cast: (value:Source)=>Target) {
return (value:Source|null):Target|null => {
export function preserveNull<Source, Target> (cast: (value: Source) => Target) {
return (value: Source | null): Target | null => {
if (value === null) {
return null
}
@ -18,11 +18,11 @@ export function preserveNull<Source, Target> (cast: (value:Source)=>Target) {
}
}
export function undefinedToDefault<Type> (defaultValue:Type): (value:Type|undefined)=>Type {
export function undefinedToDefault<Type> (defaultValue: Type): (value: Type | undefined) => Type {
return (value) => typeof value === 'undefined' ? defaultValue : value
}
export function stringTrimmed (value: string|undefined): string {
export function stringTrimmed (value: string | undefined): string {
return (value ?? '').trim().replace(/^\++|\++$/g, '')
}
@ -30,7 +30,7 @@ export function stringToInt (value: string): number {
return parseInt(value)
}
export function stringToBool (value:string):boolean {
export function stringToBool (value: string): boolean {
switch (value) {
case 'true':
case '1':
@ -42,7 +42,8 @@ export function stringToBool (value:string):boolean {
}
}
export function transform<Target> (originalSchema:ZodSchema<any>, cast:(value:unknown)=>Target, newSchema:ZodSchema<any>) {
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export function transform<Target> (originalSchema: ZodSchema<any>, cast: (value: unknown) => Target, newSchema: ZodSchema<any>) {
return originalSchema.refine(
value => {
try {

Wyświetl plik

@ -1,12 +1,12 @@
import '../styles/global.scss'
import { AppProps } from 'next/app'
import React from 'react'
import React, { ReactElement } from 'react'
import { ApolloProvider } from '@apollo/client'
import createGraphqlClient from '../graphql/client/createGraphqlClient'
const graphqlClient = createGraphqlClient()
const App = ({ Component, pageProps }: AppProps) => {
const App = ({ Component, pageProps }: AppProps): ReactElement => {
return <ApolloProvider client={graphqlClient}>
<Component {...pageProps} />
</ApolloProvider>

Wyświetl plik

@ -8,7 +8,7 @@ const graphqlServer = createGraphqlServer()
const startedServer = graphqlServer.start()
const handler = async (req: NextApiRequest, res: NextApiResponse) :Promise<void> => {
const handler = async (req: NextApiRequest, res: NextApiResponse): Promise<void> => {
if (req.method === 'OPTIONS') {
res.end()
return

Wyświetl plik

@ -1,5 +1,5 @@
import Head from 'next/head'
import React, { useEffect, useState } from 'react'
import React, { ReactElement, useEffect, useState } from 'react'
import Loader from '../components/Loader'
import FeedResults from '../components/FeedResults'
import Layout, { siteTitle } from '../components/Layout'
@ -16,7 +16,7 @@ import getMatomo from '../lib/getMatomo'
import { feedQueryInputSchema, FeedQueryInputType } from '../graphql/common/types/FeedQueryInput'
import { ListFeedsVariables } from '../graphql/common/queries/listFeeds'
const Feeds: React.FC<InferGetServerSidePropsType<typeof getServerSideProps>> = ({ matomoConfig }) => {
const Feeds: React.FC<InferGetServerSidePropsType<typeof getServerSideProps>> = ({ matomoConfig }): ReactElement => {
const router = useRouter()
const routerQuery = feedQueryInputSchema.parse(router.query)
const [page, setPage] = useState<number>(0)
@ -28,8 +28,9 @@ const Feeds: React.FC<InferGetServerSidePropsType<typeof getServerSideProps>> =
query
}
})
useEffect(() => {
useEffect((): void => {
router.push({ query })
.catch((error) => console.error(error))
getMatomo(matomoConfig).trackEvent({
category: 'feeds',
action: 'new-search'
@ -48,7 +49,7 @@ const Feeds: React.FC<InferGetServerSidePropsType<typeof getServerSideProps>> =
})
}, [page])
const handleQueryChange = (event) => {
const handleQueryChange = (event): void => {
const inputElement = event.target
const value = inputElement.value
const name = inputElement.name
@ -60,7 +61,7 @@ const Feeds: React.FC<InferGetServerSidePropsType<typeof getServerSideProps>> =
setPage(0)
}
const handleSearchSubmit = async (event) => {
const handleSearchSubmit = async (event): Promise<void> => {
event.preventDefault()
setPageLoading(true)
setPage(0)
@ -68,7 +69,7 @@ const Feeds: React.FC<InferGetServerSidePropsType<typeof getServerSideProps>> =
setPageLoading(false)
}
const handleLoadMore = async (event) => {
const handleLoadMore = async (event): Promise<void> => {
event.preventDefault()
setPageLoading(true)
await fetchMore({
@ -117,12 +118,12 @@ const Feeds: React.FC<InferGetServerSidePropsType<typeof getServerSideProps>> =
<Loader loading={loading || pageLoading} showBottom={true}>
{
data && query.search
? <FeedResults feeds={data.listFeeds.items} />
: ''
(data != null) && query.search.length > 0
? <FeedResults feeds={data.listFeeds.items}/>
: ''
}
</Loader>
{!loading && !pageLoading && data?.listFeeds?.paging?.hasNext
{!loading && !pageLoading && data?.listFeeds?.paging?.hasNext !== undefined && data?.listFeeds?.paging?.hasNext
? (
<div className={'d-flex justify-content-center'}>
<button className={'btn btn-secondary'} onClick={handleLoadMore}>
@ -132,10 +133,10 @@ const Feeds: React.FC<InferGetServerSidePropsType<typeof getServerSideProps>> =
</div>
)
: ''}
{error
{(error != null)
? (<div className={'d-flex justify-content-center'}>
<FontAwesomeIcon icon={faExclamationTriangle} className={'margin-right'}/>
<span>{error.message}</span>
<FontAwesomeIcon icon={faExclamationTriangle} className={'margin-right'}/>
<span>{error.message}</span>
</div>)
: ''}
</Layout>

Wyświetl plik

@ -21,7 +21,7 @@ import { NodeSoringByEnumType } from '../graphql/common/types/NodeSortingByEnum'
const Nodes: React.FC<InferGetServerSidePropsType<typeof getServerSideProps>> = ({ matomoConfig }) => {
const router = useRouter()
let routerQuery:NodeQueryInputType
let routerQuery: NodeQueryInputType
try {
routerQuery = nodeQueryInputSchema.parse(router.query)
} catch (e) {
@ -56,26 +56,26 @@ const Nodes: React.FC<InferGetServerSidePropsType<typeof getServerSideProps>> =
]
})
}, [page])
useEffect(() => {
router.push({ query })
useEffect((): void => {
void router.push({ query })
getMatomo(matomoConfig).trackEvent({
category: 'nodes',
action: 'new-search'
})
}, [query])
const handleQueryChange = (event) => {
const handleQueryChange = (event): void => {
const targetInput = event.target
const value = targetInput.value
const name = targetInput.name
const newQuery:NodeQueryInputType = { ...query }
const newQuery: NodeQueryInputType = { ...query }
newQuery[name] = value
console.info('Query changed', { name, value })
setQuery(newQuery)
setPage(0)
}
const handleSearchSubmit = async (event) => {
const handleSearchSubmit = async (event): Promise<void> => {
setPageLoading(true)
event.preventDefault()
setQuery(query)
@ -84,7 +84,7 @@ const Nodes: React.FC<InferGetServerSidePropsType<typeof getServerSideProps>> =
setPageLoading(false)
}
const handleLoadMore = async (event) => {
const handleLoadMore = async (event): Promise<void> => {
event.preventDefault()
setPage(page + 1)
console.info('Loading next page', { query, page })
@ -107,7 +107,7 @@ const Nodes: React.FC<InferGetServerSidePropsType<typeof getServerSideProps>> =
setPageLoading(false)
}
const toggleSort = (sortBy: NodeSoringByEnumType) => {
const toggleSort = (sortBy: NodeSoringByEnumType): void => {
const sortWay = query.sortBy === sortBy && query.sortWay === 'asc' ? 'desc' : 'asc'
getMatomo(matomoConfig).trackEvent({
category: 'nodes',
@ -155,7 +155,7 @@ const Nodes: React.FC<InferGetServerSidePropsType<typeof getServerSideProps>> =
</form>
<Loader loading={loading || pageLoading} showBottom={true}>
{
data
(data != null)
? (
<div className="table-responsive">
<table className={'table table-dark table-striped table-bordered nodes'}>
@ -212,7 +212,7 @@ const Nodes: React.FC<InferGetServerSidePropsType<typeof getServerSideProps>> =
</tr>
</thead>
<tbody>
{data.listNodes.items.length
{(data.listNodes.items.length > 0)
? data.listNodes.items.map((node, index) => {
return (
<tr key={index}>
@ -228,7 +228,7 @@ const Nodes: React.FC<InferGetServerSidePropsType<typeof getServerSideProps>> =
<td className={'text-end'}>{node.halfYearActiveUserCount ?? '?'}</td>
<td className={'text-end'}>{node.statusesCount ?? '?'}</td>
<td>{node.openRegistrations === null ? '?' : (node.openRegistrations ? 'Opened' : 'Closed')}</td>
<td>{node.refreshedAt ? (new Date(node.refreshedAt)).toLocaleDateString() : 'Never'}</td>
<td>{node.refreshedAt !== '' ? (new Date(node.refreshedAt)).toLocaleDateString() : 'Never'}</td>
</tr>
)
})
@ -244,7 +244,7 @@ const Nodes: React.FC<InferGetServerSidePropsType<typeof getServerSideProps>> =
: ''
}
</Loader>
{data?.listNodes?.paging?.hasNext && !loading && !pageLoading
{!loading && !pageLoading && data?.listNodes?.paging?.hasNext !== undefined && data?.listNodes?.paging?.hasNext
? (
<div className={'d-flex justify-content-center'}>
<button className={'btn btn-secondary'} onClick={handleLoadMore}>
@ -254,7 +254,7 @@ const Nodes: React.FC<InferGetServerSidePropsType<typeof getServerSideProps>> =
</div>
)
: ''}
{error
{(error != null)
? (<div className={'d-flex justify-content-center'}>
<FontAwesomeIcon icon={faExclamationTriangle} className={'margin-right'}/>
<span>{error.message}</span>

Wyświetl plik

@ -37,14 +37,14 @@ const Stats: React.FC<InferGetServerSidePropsType<typeof getServerSideProps>> =
})
useEffect(() => {
router.push({ query })
void router.push({ query })
getMatomo(matomoConfig).trackEvent({
category: 'stats',
action: 'new-search'
})
}, [query])
const toggleSort = (sortBy: StatsSoringByEnumType) => {
const toggleSort = (sortBy: StatsSoringByEnumType): void => {
const sortWay = query.sortBy === sortBy && query.sortWay === 'asc' ? 'desc' : 'asc'
getMatomo(matomoConfig).trackEvent({
category: 'stats',
@ -73,7 +73,7 @@ const Stats: React.FC<InferGetServerSidePropsType<typeof getServerSideProps>> =
accountFeedCount: 0,
channelFeedCount: 0
}
if (data) {
if (data != null) {
data.listStats.items.forEach(item => {
if (item.softwareName === null) {
return
@ -119,7 +119,7 @@ const Stats: React.FC<InferGetServerSidePropsType<typeof getServerSideProps>> =
</tr>
</thead>
<Loader loading={loading} table={4} showTop={true} hideContent={true}>
{!data
{(data == null)
? (
<tbody>
<tr>
@ -183,7 +183,7 @@ const Stats: React.FC<InferGetServerSidePropsType<typeof getServerSideProps>> =
}
</Loader>
</table>
{error
{(error != null)
? (<div className={'d-flex justify-content-center'}>
<FontAwesomeIcon icon={faExclamationTriangle} className={'margin-right'}/>
<span>{error.message}</span>

Wyświetl plik

@ -1,4 +1,4 @@
export type Sort = {
sortBy?: string,
sortWay?: 'asc' | 'desc'
export interface Sort {
sortBy?: string
sortWay?: 'asc' | 'desc'
}

Wyświetl plik

@ -17,6 +17,7 @@
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"strictNullChecks": true,
"jsx": "preserve"
},
"include": [

Wyświetl plik

@ -1,11 +0,0 @@
{
"defaultSeverity": "error",
"extends": [
"tslint:recommended"
],
"jsRules": {},
"rules": {
"no-console": false
},
"rulesDirectory": []
}

5576
application/yarn.lock 100644

Plik diff jest za duży Load Diff