Compare commits

...

16 Commits

Author SHA1 Message Date
3f5451b265 Merge branch 'staging' into 130-fix-empty-relay-issue
Some checks failed
Open PR on Staging / audit_and_check (pull_request) Failing after 32s
2024-08-07 17:55:22 +03:00
8d683de88c Revert "chore: prettier"
This reverts commit 7e420ef583.
2024-08-07 17:54:45 +03:00
c1a6ac246b Merge pull request 'CI - Add git hooks and staging checks' (#128) from issue-38-90-pipeline-check into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m11s
Reviewed-on: #128
Reviewed-by: Stixx <m@noreply.git.nostrdev.com>
2024-08-07 13:54:35 +00:00
1297acc7db refactor: PR review changes and lint fixes
All checks were successful
Open PR on Staging / audit_and_check (pull_request) Successful in 33s
2024-08-07 15:18:25 +02:00
e3eb1f37c1 ci(prettier): changes after running npm run formatter:fix 2024-08-07 15:18:25 +02:00
57aba9dd42 chore(lint): increase max warnings to 41 2024-08-07 15:18:25 +02:00
312ec38817 ci: add synchronize to PR triggers 2024-08-07 15:18:25 +02:00
dde019d364 chore(ci): add license 2024-08-07 15:18:25 +02:00
d43067f70e fix(ci): run lint-staged always, fix lint-stage commands 2024-08-07 15:18:25 +02:00
84d13793ff feat(ci): add lint-staged in pre-commit 2024-08-07 15:18:25 +02:00
5290dda52a feat(ci): add open pr workflow 2024-08-07 15:18:25 +02:00
4af578133c fix(ci): add license check in staging workflow 2024-08-07 15:18:25 +02:00
9ca75c7a52 chore(ci): add licenseChecker and check in staging workflow 2024-08-07 15:18:25 +02:00
7a0e1c9052 chore(ci): add audit, lint and prettier checks to staging workflow 2024-08-07 15:18:25 +02:00
ea7fde4b38 fix(ci): fix hook colors 2024-08-07 15:18:25 +02:00
70f625ffd1 feat(ci): add git hooks 2024-08-07 15:18:25 +02:00
12 changed files with 1296 additions and 122 deletions

View File

@ -6,7 +6,7 @@ module.exports = {
'plugin:@typescript-eslint/recommended', 'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended' 'plugin:react-hooks/recommended'
], ],
ignorePatterns: ['dist', '.eslintrc.cjs'], ignorePatterns: ['dist', '.eslintrc.cjs', 'licenseChecker.cjs'],
parser: '@typescript-eslint/parser', parser: '@typescript-eslint/parser',
plugins: ['react-refresh'], plugins: ['react-refresh'],
rules: { rules: {

19
.git-hooks/commit-msg Normal file
View File

@ -0,0 +1,19 @@
#!/bin/sh
# Get the commit message (the parameter we're given is just the path to the
# temporary file which holds the message).
commit_message=$(cat "$1")
if (echo "$commit_message" | grep -Eq "^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\([a-z0-9 \-]+\))?!?: .+$") then
tput setaf 2;
echo -e "${GREEN} ✔ Commit message meets Conventional Commit standards"
tput sgr0;
exit 0
fi
tput setaf 1;
echo -e "${RED}❌ Commit message does not meet the Conventional Commit standard!"
tput sgr0;
echo "An example of a valid message is:"
echo " feat(login): add the 'remember me' button"
echo " More details at: https://www.conventionalcommits.org/en/v1.0.0/#summary"
exit 1

13
.git-hooks/pre-commit Normal file
View File

@ -0,0 +1,13 @@
#!/bin/sh
# Avoid commits to the master branch
BRANCH=`git rev-parse --abbrev-ref HEAD`
REGEX="^(master|main|staging|development)$"
if [[ "$BRANCH" =~ $REGEX ]]; then
echo "You are on branch $BRANCH. Are you sure you want to commit to this branch?"
echo "If so, commit with -n to bypass the pre-commit hook."
exit 1
fi
npm run lint-staged

View File

@ -17,9 +17,21 @@ jobs:
with: with:
node-version: 18 node-version: 18
- name: Audit
run: npm audit
- name: Install Dependencies - name: Install Dependencies
run: npm ci run: npm ci
- name: License check
run: npm run license-checker
- name: Lint check
run: npm run lint
- name: Formatter check
run: npm run formatter:check
- name: Create .env File - name: Create .env File
run: echo "VITE_MOST_POPULAR_RELAYS=${{ vars.VITE_MOST_POPULAR_RELAYS }}" > .env run: echo "VITE_MOST_POPULAR_RELAYS=${{ vars.VITE_MOST_POPULAR_RELAYS }}" > .env
@ -29,6 +41,6 @@ jobs:
- name: Release Build - name: Release Build
run: | run: |
npm -g install cloudron-surfer npm -g install cloudron-surfer
surfer config --token ${{ secrets.STAGING_CLOUDRON_SURFER_TOKEN }} --server staging.sigit.io surfer config --token ${{ secrets.STAGING_CLOUDRON_SURFER_TOKEN }} --server staging.sigit.io
surfer put dist/* / --all -d surfer put dist/* / --all -d
surfer put dist/.well-known / --all surfer put dist/.well-known / --all

View File

@ -0,0 +1,34 @@
name: Open PR on Staging
on:
pull_request:
types: [opened, edited, synchronize]
branches:
- staging
jobs:
audit_and_check:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Node
uses: actions/setup-node@v2
with:
node-version: 18
- name: Audit
run: npm audit
- name: Install Dependencies
run: npm ci
- name: License check
run: npm run license-checker
- name: Lint check
run: npm run lint
- name: Formatter check
run: npm run formatter:check

28
licenseChecker.cjs Normal file
View File

@ -0,0 +1,28 @@
const process = require('node:process')
const licenseChecker = require('license-checker')
const check = (cwd) => {
return new Promise((resolve, reject) => {
licenseChecker.init(
{
production: true,
start: cwd,
excludePrivatePackages: true,
onlyAllow:
'AFLv2.1;Apache 2.0;Apache-2.0;Apache*;Artistic-2.0;0BSD;BSD*;BSD-2-Clause;BSD-3-Clause;BSD 3-Clause;CC0-1.0;CC-BY-3.0;CC-BY-4.0;ISC;MIT;MPL-2.0;ODC-By-1.0;Python-2.0;Unlicense;',
excludePackages: ''
},
(error, json) => {
if (error) {
reject(error)
} else {
resolve(json)
}
}
)
})
}
check(process.cwd(), true)
.then(() => console.log('All packages are licensed properly'))
.catch((err) => console.log('license checker err', err))

1094
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -7,11 +7,16 @@
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "tsc && vite build", "build": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 41",
"lint:fix": "eslint . --fix --ext ts,tsx --report-unused-disable-directives --max-warnings 0", "lint:fix": "eslint . --fix --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"lint:staged": "eslint --fix --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"formatter:check": "prettier --check \"src/**/*.{ts,tsx,js,jsx,html,css,sass,less,yml,md,graphql}\"", "formatter:check": "prettier --check \"src/**/*.{ts,tsx,js,jsx,html,css,sass,less,yml,md,graphql}\"",
"formatter:fix": "prettier --write \"src/**/*.{ts,tsx,js,jsx,html,css,sass,less,yml,md,graphql}\"", "formatter:fix": "prettier --write \"src/**/*.{ts,tsx,js,jsx,html,css,sass,less,yml,md,graphql}\"",
"preview": "vite preview" "formatter:staged": "prettier --write --ignore-unknown",
"preview": "vite preview",
"preinstall": "git config core.hooksPath .git-hooks",
"license-checker": "node licenseChecker.cjs",
"lint-staged": "lint-staged"
}, },
"dependencies": { "dependencies": {
"@emotion/react": "11.11.4", "@emotion/react": "11.11.4",
@ -61,10 +66,18 @@
"eslint": "^8.56.0", "eslint": "^8.56.0",
"eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.5", "eslint-plugin-react-refresh": "^0.4.5",
"license-checker": "^25.0.1",
"lint-staged": "^15.2.8",
"prettier": "3.2.5", "prettier": "3.2.5",
"ts-css-modules-vite-plugin": "1.0.20", "ts-css-modules-vite-plugin": "1.0.20",
"typescript": "^5.2.2", "typescript": "^5.2.2",
"vite": "^5.1.4", "vite": "^5.1.4",
"vite-tsconfig-paths": "4.3.2" "vite-tsconfig-paths": "4.3.2"
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"npm run lint:staged"
],
"*.{ts,tsx,js,jsx,html,css,sass,less,yml,md,graphql}": "npm run formatter:staged"
} }
} }

View File

@ -164,18 +164,13 @@ export class MetadataController extends EventEmitter {
* or a fallback RelaySet with Sigit's Relay * or a fallback RelaySet with Sigit's Relay
*/ */
public findRelayListMetadata = async (hexKey: string): Promise<RelaySet> => { public findRelayListMetadata = async (hexKey: string): Promise<RelaySet> => {
const relayEvent = const relayEvent = await findRelayListInCache(hexKey)
(await findRelayListInCache(hexKey)) || || await findRelayListAndUpdateCache([this.specialMetadataRelay], hexKey)
(await findRelayListAndUpdateCache( || await findRelayListAndUpdateCache(await this.nostrController.getMostPopularRelays(), hexKey)
[this.specialMetadataRelay],
hexKey
)) ||
(await findRelayListAndUpdateCache(
await this.nostrController.getMostPopularRelays(),
hexKey
))
return relayEvent ? getUserRelaySet(relayEvent.tags) : getDefaultRelaySet() return relayEvent
? getUserRelaySet(relayEvent.tags)
: getDefaultRelaySet()
} }
public extractProfileMetadataContent = (event: Event) => { public extractProfileMetadataContent = (event: Event) => {

View File

@ -91,8 +91,7 @@ export const RelaysPage = () => {
if (isMounted) { if (isMounted) {
if ( if (
!relaysState?.mapUpdated || !relaysState?.mapUpdated ||
(newRelayMap?.mapUpdated !== undefined && newRelayMap?.mapUpdated !== undefined && (newRelayMap?.mapUpdated > relaysState?.mapUpdated)
newRelayMap?.mapUpdated > relaysState?.mapUpdated)
) { ) {
if ( if (
!relaysState?.map || !relaysState?.map ||

View File

@ -5,7 +5,7 @@ import JSZip from 'jszip'
import _ from 'lodash' import _ from 'lodash'
import { MuiFileInput } from 'mui-file-input' import { MuiFileInput } from 'mui-file-input'
import { Event, verifyEvent } from 'nostr-tools' import { Event, verifyEvent } from 'nostr-tools'
import { useEffect, useState } from 'react' import { useCallback, useEffect, useState } from 'react'
import { useSelector } from 'react-redux' import { useSelector } from 'react-redux'
import { useLocation, useNavigate } from 'react-router-dom' import { useLocation, useNavigate } from 'react-router-dom'
import { toast } from 'react-toastify' import { toast } from 'react-toastify'
@ -208,7 +208,6 @@ export const SignPage = () => {
const signedMarks = extractMarksFromSignedMeta(meta) const signedMarks = extractMarksFromSignedMeta(meta)
const currentUserMarks = getCurrentUserMarks(metaMarks, signedMarks) const currentUserMarks = getCurrentUserMarks(metaMarks, signedMarks)
setCurrentUserMarks(currentUserMarks) setCurrentUserMarks(currentUserMarks)
// setCurrentUserMark(findNextCurrentUserMark(currentUserMarks) || null)
setIsReadyToSign(isCurrentUserMarksComplete(currentUserMarks)) setIsReadyToSign(isCurrentUserMarksComplete(currentUserMarks))
} }
@ -218,7 +217,79 @@ export const SignPage = () => {
if (meta) { if (meta) {
handleUpdatedMeta(meta) handleUpdatedMeta(meta)
} }
}, [meta]) }, [meta, usersPubkey])
const decrypt = useCallback(
async (file: File) => {
setLoadingSpinnerDesc('Decrypting file')
const zip = await loadZip(file)
if (!zip) return
const parsedKeysJson = await parseKeysJson(zip)
if (!parsedKeysJson) return
const encryptedArrayBuffer = await readContentOfZipEntry(
zip,
'compressed.sigit',
'arraybuffer'
)
if (!encryptedArrayBuffer) return
const { keys, sender } = parsedKeysJson
for (const key of keys) {
// Set up event listener for authentication event
nostrController.on('nsecbunker-auth', (url) => {
setAuthUrl(url)
})
// Set up timeout promise to handle encryption timeout
const timeoutPromise = new Promise<never>((_, reject) => {
setTimeout(() => {
reject(new Error('Timeout occurred'))
}, 60000) // Timeout duration = 60 seconds
})
// decrypt the encryptionKey, with timeout
const encryptionKey = await Promise.race([
nostrController.nip04Decrypt(sender, key),
timeoutPromise
])
.then((res) => {
return res
})
.catch((err) => {
console.log('err :>> ', err)
return null
})
.finally(() => {
setAuthUrl(undefined) // Clear authentication URL
})
// Return if encryption failed
if (!encryptionKey) continue
const arrayBuffer = await decryptArrayBuffer(
encryptedArrayBuffer,
encryptionKey
)
.catch((err) => {
console.log('err in decryption:>> ', err)
return null
})
.finally(() => {
setIsLoading(false)
})
if (arrayBuffer) return arrayBuffer
}
return null
},
[nostrController]
)
useEffect(() => { useEffect(() => {
// online mode - from create and home page views // online mode - from create and home page views
@ -276,7 +347,7 @@ export const SignPage = () => {
setIsLoading(false) setIsLoading(false)
setDisplayInput(true) setDisplayInput(true)
} }
}, [decryptedArrayBuffer, uploadedZip, metaInNavState]) }, [decryptedArrayBuffer, uploadedZip, metaInNavState, decrypt])
const handleArrayBufferFromBlossom = async ( const handleArrayBufferFromBlossom = async (
arrayBuffer: ArrayBuffer, arrayBuffer: ArrayBuffer,
@ -354,75 +425,6 @@ export const SignPage = () => {
}) })
} }
const decrypt = async (file: File) => {
setLoadingSpinnerDesc('Decrypting file')
const zip = await loadZip(file)
if (!zip) return
const parsedKeysJson = await parseKeysJson(zip)
if (!parsedKeysJson) return
const encryptedArrayBuffer = await readContentOfZipEntry(
zip,
'compressed.sigit',
'arraybuffer'
)
if (!encryptedArrayBuffer) return
const { keys, sender } = parsedKeysJson
for (const key of keys) {
// Set up event listener for authentication event
nostrController.on('nsecbunker-auth', (url) => {
setAuthUrl(url)
})
// Set up timeout promise to handle encryption timeout
const timeoutPromise = new Promise<never>((_, reject) => {
setTimeout(() => {
reject(new Error('Timeout occurred'))
}, 60000) // Timeout duration = 60 seconds
})
// decrypt the encryptionKey, with timeout
const encryptionKey = await Promise.race([
nostrController.nip04Decrypt(sender, key),
timeoutPromise
])
.then((res) => {
return res
})
.catch((err) => {
console.log('err :>> ', err)
return null
})
.finally(() => {
setAuthUrl(undefined) // Clear authentication URL
})
// Return if encryption failed
if (!encryptionKey) continue
const arrayBuffer = await decryptArrayBuffer(
encryptedArrayBuffer,
encryptionKey
)
.catch((err) => {
console.log('err in decryption:>> ', err)
return null
})
.finally(() => {
setIsLoading(false)
})
if (arrayBuffer) return arrayBuffer
}
return null
}
const handleDecryptedArrayBuffer = async (arrayBuffer: ArrayBuffer) => { const handleDecryptedArrayBuffer = async (arrayBuffer: ArrayBuffer) => {
const decryptedZipFile = new File([arrayBuffer], 'decrypted.zip') const decryptedZipFile = new File([arrayBuffer], 'decrypted.zip')
@ -618,10 +620,12 @@ export const SignPage = () => {
} }
// Handle errors during zip file generation // Handle errors during zip file generation
const handleZipError = (err: any) => { const handleZipError = (err: unknown) => {
console.log('Error in zip:>> ', err) console.log('Error in zip:>> ', err)
setIsLoading(false) setIsLoading(false)
toast.error(err.message || 'Error occurred in generating zip file') if (err instanceof Error) {
toast.error(err.message || 'Error occurred in generating zip file')
}
return null return null
} }

View File

@ -5,8 +5,8 @@ import { localCache } from '../services'
import { ONE_WEEK_IN_MS, SIGIT_RELAY } from './const.ts' import { ONE_WEEK_IN_MS, SIGIT_RELAY } from './const.ts'
import { RelayMap, RelaySet } from '../types' import { RelayMap, RelaySet } from '../types'
const READ_MARKER = 'read' const READ_MARKER = "read"
const WRITE_MARKET = 'write' const WRITE_MARKET = "write"
/** /**
* Attempts to find a relay list from the provided lookUpRelays. * Attempts to find a relay list from the provided lookUpRelays.
@ -15,10 +15,7 @@ const WRITE_MARKET = 'write'
* @param hexKey * @param hexKey
* @return found relay list or null * @return found relay list or null
*/ */
const findRelayListAndUpdateCache = async ( const findRelayListAndUpdateCache = async (lookUpRelays: string[], hexKey: string): Promise<Event | null> => {
lookUpRelays: string[],
hexKey: string
): Promise<Event | null> => {
try { try {
const eventFilter: Filter = { const eventFilter: Filter = {
kinds: [RelayList], kinds: [RelayList],
@ -45,14 +42,10 @@ const findRelayListAndUpdateCache = async (
const findRelayListInCache = async (hexKey: string): Promise<Event | null> => { const findRelayListInCache = async (hexKey: string): Promise<Event | null> => {
try { try {
// Attempt to retrieve the metadata event from the local cache // Attempt to retrieve the metadata event from the local cache
const cachedRelayListMetadataEvent = const cachedRelayListMetadataEvent = await localCache.getUserRelayListMetadata(hexKey)
await localCache.getUserRelayListMetadata(hexKey)
// Check if the cached event is not older than one week // Check if the cached event is not older than one week
if ( if (cachedRelayListMetadataEvent && isOlderThanOneWeek(cachedRelayListMetadataEvent.cachedAt)) {
cachedRelayListMetadataEvent &&
isOlderThanOneWeek(cachedRelayListMetadataEvent.cachedAt)
) {
return cachedRelayListMetadataEvent.event return cachedRelayListMetadataEvent.event
} }
@ -111,5 +104,5 @@ export {
findRelayListInCache, findRelayListInCache,
getUserRelaySet, getUserRelaySet,
getDefaultRelaySet, getDefaultRelaySet,
getDefaultRelayMap getDefaultRelayMap,
} }