mirror of
https://github.com/reconurge/flowsint.git
synced 2026-03-12 01:44:42 -05:00
Refresh ignored files
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,6 +0,0 @@
|
||||
import { clsx, type ClassValue } from "clsx"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import type React from "react"
|
||||
|
||||
import { useState } from "react"
|
||||
import { useRouter } from "next/navigation"
|
||||
import { createNewProject } from "@/lib/actions/project"
|
||||
import { createNewProject } from "@/lib/actions/projects"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import {
|
||||
Dialog,
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
'use server'
|
||||
|
||||
import { revalidatePath } from 'next/cache'
|
||||
import { redirect } from 'next/navigation'
|
||||
import { createClient } from '@/lib/supabase/server'
|
||||
|
||||
export async function signInWithGithub() {
|
||||
const supabase = await createClient()
|
||||
const { data } = await supabase.auth.signInWithOAuth({
|
||||
provider: 'github',
|
||||
options: {
|
||||
redirectTo: process.env.NEXT_PUBLIC_AUTH_REDIRECT,
|
||||
},
|
||||
})
|
||||
if (data.url) {
|
||||
redirect(data.url)
|
||||
}
|
||||
}
|
||||
|
||||
export async function login(formData: FormData) {
|
||||
const supabase = await createClient()
|
||||
|
||||
// type-casting here for convenience
|
||||
// in practice, you should validate your inputs
|
||||
const data = {
|
||||
email: formData.get('email') as string,
|
||||
password: formData.get('password') as string,
|
||||
}
|
||||
|
||||
const { error } = await supabase.auth.signInWithPassword(data)
|
||||
|
||||
if (error) {
|
||||
redirect('/error')
|
||||
}
|
||||
// revalidatePath('/', 'layout')
|
||||
// redirect('/')
|
||||
}
|
||||
|
||||
export async function signup(formData: FormData) {
|
||||
const supabase = await createClient()
|
||||
|
||||
// type-casting here for convenience
|
||||
// in practice, you should validate your inputs
|
||||
const data = {
|
||||
email: formData.get('email') as string,
|
||||
password: formData.get('password') as string,
|
||||
}
|
||||
|
||||
const { error } = await supabase.auth.signUp(data)
|
||||
|
||||
if (error) {
|
||||
redirect('/error')
|
||||
}
|
||||
|
||||
revalidatePath('/', 'layout')
|
||||
redirect('/')
|
||||
}
|
||||
|
||||
export async function logout() {
|
||||
const supabase = await createClient()
|
||||
|
||||
const { error } = await supabase.auth.signOut()
|
||||
|
||||
if (error) {
|
||||
redirect('/error')
|
||||
}
|
||||
|
||||
revalidatePath('/', 'layout')
|
||||
redirect('/login')
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
"use server"
|
||||
import { createClient } from "../supabase/server"
|
||||
import { redirect } from "next/navigation"
|
||||
import { revalidatePath } from "next/cache"
|
||||
|
||||
export async function createNewCase(formData: FormData, project_id: string) {
|
||||
const supabase = await createClient()
|
||||
const { data: session, error: userError } = await supabase.auth.getUser()
|
||||
if (userError || !session?.user) {
|
||||
redirect('/login')
|
||||
}
|
||||
const data = {
|
||||
title: formData.get("title"),
|
||||
description: formData.get("description"),
|
||||
}
|
||||
try {
|
||||
const { data: investigation, error } = await supabase.from("investigations").insert({ ...data, owner_id: session?.user?.id, project_id }).select("id").single()
|
||||
if (error) throw error
|
||||
revalidatePath("/")
|
||||
return { success: true, id: investigation.id }
|
||||
} catch (error) {
|
||||
console.error("Error creating new case:", error)
|
||||
return { success: false, error: "Failed to create new case" }
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
'use server'
|
||||
import { createClient } from "../supabase/server";
|
||||
|
||||
export async function checkEmail(email: string, investigation_id: string) {
|
||||
const url = `${process.env.NEXT_PUBLIC_DOCKER_FLOWSINT_API}/scan/`;
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
"email": email,
|
||||
"investigation_id": investigation_id
|
||||
})
|
||||
},
|
||||
);
|
||||
return response.json();
|
||||
}
|
||||
|
||||
async function checkBreachedAccount(account: string | number | boolean, apiKey: string, appName: string) {
|
||||
const url = `https://haveibeenpwned.com/api/v3/breachedaccount/${encodeURIComponent(account)}?truncateResponse=false`;
|
||||
const supabase = await createClient()
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'hibp-api-key': apiKey,
|
||||
'user-agent': appName
|
||||
}
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json() as any[];
|
||||
for (const breach of data) {
|
||||
// insert a new breach if not exist
|
||||
const { error } = await supabase.from('breaches').upsert({
|
||||
name: breach.Name, description: breach.Description, raw_content: breach
|
||||
}, { onConflict: 'name' })
|
||||
if (error) return error
|
||||
// then get the breach and upsert a new relation with account
|
||||
const { data: br, error: breachError } = await supabase.from('breaches')
|
||||
.select('id')
|
||||
.eq("name", breach.Name).single()
|
||||
if (breachError) return breachError
|
||||
if (br) {
|
||||
// finally insert new relation between account and breach
|
||||
const { error: relationError } = await supabase.from('accounts_breaches').upsert({
|
||||
account: account, breach_id: br.id
|
||||
}, {
|
||||
onConflict: 'account,breach_id'
|
||||
})
|
||||
if (relationError) return relationError
|
||||
}
|
||||
|
||||
}
|
||||
return data;
|
||||
} else if (response.status === 404) {
|
||||
return `Good news! Account ${account} hasn't been found in any breaches.`;
|
||||
} else {
|
||||
return `Error: ${response.status} - ${response.statusText}`;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('An error occurred:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const apiKey = process.env.HIBP_API_KEY || ""
|
||||
const appName = 'MyHIBPChecker';
|
||||
|
||||
export async function investigateHIBPwd(username: string) {
|
||||
if (!username) {
|
||||
throw new Error("Malformed query.")
|
||||
}
|
||||
return checkBreachedAccount(username, apiKey, appName);
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
import { supabase } from "@/lib/supabase/client"
|
||||
import { useQuery } from "@supabase-cache-helpers/postgrest-swr"
|
||||
|
||||
export function useEmailsAndBreaches(individualId: string | null | undefined) {
|
||||
const { data: emails, error } = useQuery(
|
||||
individualId ? supabase.from("individuals").select(`*, emails(*)`).eq("id", individualId).single() : null,
|
||||
{
|
||||
revalidateOnFocus: false,
|
||||
revalidateOnReconnect: false,
|
||||
},
|
||||
)
|
||||
|
||||
const emailAddresses = emails?.emails?.map(({ email }: { email: string }) => email) || []
|
||||
|
||||
const { data: breaches, error: breachesError, mutate } = useQuery(
|
||||
emailAddresses.length > 0
|
||||
? supabase.from("accounts_breaches").select(`account, breach:breach_id(raw_content)`).in("account", emailAddresses)
|
||||
: null,
|
||||
{
|
||||
revalidateOnFocus: false,
|
||||
revalidateOnReconnect: false,
|
||||
},
|
||||
)
|
||||
|
||||
const formattedData = emailAddresses.map((email: string) => ({
|
||||
email,
|
||||
breaches: breaches?.filter((breach: any) => breach.account === email).map((breach: any) => breach.breach.raw_content) || [],
|
||||
}))
|
||||
|
||||
const isLoading = !emails && !error
|
||||
|
||||
return {
|
||||
emails: formattedData,
|
||||
isLoading,
|
||||
refetch: mutate,
|
||||
error: error || breachesError,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
import { supabase } from "@/lib/supabase/client";
|
||||
import { useQuery } from "@supabase-cache-helpers/postgrest-swr";
|
||||
|
||||
export function useIndividual(individualId: string | null | undefined) {
|
||||
const { data: individual, mutate, isLoading, error } = useQuery(Boolean(individualId) ?
|
||||
supabase
|
||||
.from('individuals')
|
||||
.select(`
|
||||
*,
|
||||
ip_addresses(*),
|
||||
phone_numbers(*),
|
||||
social_accounts(*),
|
||||
emails(*)
|
||||
`)
|
||||
.eq("id", individualId)
|
||||
.single() : null,
|
||||
{
|
||||
revalidateOnFocus: false,
|
||||
revalidateOnReconnect: false,
|
||||
}
|
||||
);
|
||||
return {
|
||||
individual: individual || null,
|
||||
isLoading,
|
||||
refetch: mutate,
|
||||
error: error
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
import { supabase } from "@/lib/supabase/client";
|
||||
import { useQuery } from "@supabase-cache-helpers/postgrest-swr";
|
||||
|
||||
export function useRelations(individualId: string | null | undefined) {
|
||||
const { data: individuals, mutate, isLoading, error } = useQuery(
|
||||
Boolean(individualId)
|
||||
? supabase
|
||||
.from("relationships")
|
||||
.select(`
|
||||
id,
|
||||
individual_a(id, full_name, image_url),
|
||||
individual_b(id, full_name, image_url),
|
||||
relation_type,
|
||||
confidence_level
|
||||
`)
|
||||
.or(`individual_a.eq.${individualId},individual_b.eq.${individualId}`)
|
||||
: null,
|
||||
{
|
||||
revalidateOnFocus: false,
|
||||
revalidateOnReconnect: false,
|
||||
}
|
||||
);
|
||||
const relations = individuals
|
||||
? individuals.map((rel: { individual_a: any, individual_b: any, relation_type: string }) => {
|
||||
return rel.individual_a?.id === individualId
|
||||
? { ...rel.individual_b, relation_type: rel.relation_type }
|
||||
: { ...rel.individual_a, relation_type: rel.relation_type }
|
||||
}).filter(Boolean)
|
||||
: [];
|
||||
return {
|
||||
relations,
|
||||
isLoading,
|
||||
refetch: mutate,
|
||||
error
|
||||
};
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
import { supabase } from "@/lib/supabase/client";
|
||||
import { useQuery } from "@supabase-cache-helpers/postgrest-swr";
|
||||
|
||||
export function useInvestigations(project_id: string) {
|
||||
const { data: investigations, count, mutate, isLoading, error } = useQuery(
|
||||
supabase
|
||||
.from('investigations')
|
||||
.select('id, title, description')
|
||||
.eq("project_id", project_id)
|
||||
.order("last_updated_at", { ascending: false }),
|
||||
{
|
||||
revalidateOnFocus: false,
|
||||
revalidateOnReconnect: false,
|
||||
}
|
||||
);
|
||||
|
||||
return { investigations, count, isLoading, refetch: mutate, error };
|
||||
}
|
||||
|
||||
export function useInvestigation(investigationId: string | string[] | undefined) {
|
||||
if (!investigationId) return { investigation: null }
|
||||
const { data: investigation, mutate, isLoading, error } = useQuery(
|
||||
supabase
|
||||
.from('investigations')
|
||||
.select('id, title, description')
|
||||
.eq("id", investigationId)
|
||||
.single(),
|
||||
{
|
||||
revalidateOnFocus: false,
|
||||
revalidateOnReconnect: false,
|
||||
}
|
||||
);
|
||||
return { investigation: investigation ?? null, isLoading, refetch: mutate, error };
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import { supabase } from "@/lib/supabase/client";
|
||||
import { useQuery } from "@supabase-cache-helpers/postgrest-swr";
|
||||
|
||||
export function useEmails(investigationId: string | null | undefined) {
|
||||
const { data: emails, mutate, isLoading, error } = useQuery(Boolean(investigationId) ?
|
||||
supabase
|
||||
.from('emails')
|
||||
.select(`
|
||||
*
|
||||
`)
|
||||
.eq("investigation_id", investigationId)
|
||||
: null,
|
||||
{
|
||||
revalidateOnFocus: false,
|
||||
revalidateOnReconnect: false,
|
||||
}
|
||||
);
|
||||
return {
|
||||
emails: emails || null,
|
||||
isLoading,
|
||||
refetch: mutate,
|
||||
error: error
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
import { supabase } from "@/lib/supabase/client";
|
||||
import { useQuery } from "@supabase-cache-helpers/postgrest-swr";
|
||||
|
||||
export function useIndividuals(investigationId: string | null | undefined) {
|
||||
const { data: individuals, mutate, isLoading, error } = useQuery(Boolean(investigationId) ?
|
||||
supabase
|
||||
.from('individuals')
|
||||
.select(`
|
||||
*
|
||||
`)
|
||||
.eq("investigation_id", investigationId)
|
||||
: null,
|
||||
{
|
||||
revalidateOnFocus: false,
|
||||
revalidateOnReconnect: false,
|
||||
}
|
||||
);
|
||||
return {
|
||||
individuals: individuals || null,
|
||||
isLoading,
|
||||
refetch: mutate,
|
||||
error: error
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
import { supabase } from "@/lib/supabase/client";
|
||||
import { useQuery } from "@supabase-cache-helpers/postgrest-swr";
|
||||
|
||||
export function usePhones(investigationId: string | null | undefined) {
|
||||
const { data: phones, mutate, isLoading, error } = useQuery(Boolean(investigationId) ?
|
||||
supabase
|
||||
.from('phone_numbers')
|
||||
.select(`
|
||||
*
|
||||
`)
|
||||
.eq("investigation_id", investigationId)
|
||||
: null,
|
||||
{
|
||||
revalidateOnFocus: false,
|
||||
revalidateOnReconnect: false,
|
||||
}
|
||||
);
|
||||
return {
|
||||
phones: phones || null,
|
||||
isLoading,
|
||||
refetch: mutate,
|
||||
error: error
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
import { supabase } from "@/lib/supabase/client";
|
||||
import { useQuery } from "@supabase-cache-helpers/postgrest-swr";
|
||||
|
||||
export function useSearchResults(search: string | undefined, investigationId: string | string[] | undefined) {
|
||||
const { data: results, count, mutate, isLoading, error } = useQuery(search ?
|
||||
supabase
|
||||
.from('individuals')
|
||||
.select('*')
|
||||
.eq("investigation_id", investigationId)
|
||||
.ilike('full_name', `%${search}%`) : null,
|
||||
{
|
||||
revalidateOnFocus: false,
|
||||
revalidateOnReconnect: false,
|
||||
}
|
||||
);
|
||||
if (error) throw error
|
||||
|
||||
return { results, count, isLoading, refetch: mutate, error };
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import { supabase } from "@/lib/supabase/client";
|
||||
import { useQuery } from "@supabase-cache-helpers/postgrest-swr";
|
||||
|
||||
export function useSocials(investigationId: string | null | undefined) {
|
||||
const { data: socials, mutate, isLoading, error } = useQuery(Boolean(investigationId) ?
|
||||
supabase
|
||||
.from('social_accounts')
|
||||
.select(`
|
||||
*
|
||||
`)
|
||||
.eq("investigation_id", investigationId)
|
||||
: null,
|
||||
{
|
||||
revalidateOnFocus: false,
|
||||
revalidateOnReconnect: false,
|
||||
}
|
||||
);
|
||||
return {
|
||||
socials: socials || null,
|
||||
isLoading,
|
||||
refetch: mutate,
|
||||
error: error
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
import { supabase } from "@/lib/supabase/client";
|
||||
import { useQuery } from "@supabase-cache-helpers/postgrest-swr";
|
||||
|
||||
export function useUsernames(investigationId: string | string[] | undefined) {
|
||||
if (!investigationId) return { usernames: [] }
|
||||
const { data: usernames, count, mutate, isLoading, error } = useQuery(
|
||||
supabase
|
||||
.from('social_accounts')
|
||||
.select('id, username')
|
||||
.eq("investigation_id", investigationId)
|
||||
.not('username', 'is', null)
|
||||
.neq('username', ''),
|
||||
{
|
||||
revalidateOnFocus: false,
|
||||
revalidateOnReconnect: false,
|
||||
}
|
||||
);
|
||||
if (error) throw error
|
||||
|
||||
return { usernames, count, isLoading, refetch: mutate, error };
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
import { Dispatch, SetStateAction, useEffect, useState } from 'react';
|
||||
|
||||
export default function useLocalStorage<T>(key: string, initialValue: T): [T, Dispatch<SetStateAction<T>>] {
|
||||
const [storedValue, setStoredValue] = useState(initialValue);
|
||||
const [firstLoadDone, setFirstLoadDone] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const fromLocal = () => {
|
||||
if (typeof window === 'undefined') {
|
||||
return initialValue;
|
||||
}
|
||||
try {
|
||||
const item = window.localStorage.getItem(key);
|
||||
return item ? JSON.parse(item) as T : initialValue;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return initialValue;
|
||||
}
|
||||
};
|
||||
|
||||
// Only set stored value if it hasn't been set yet (on initial load)
|
||||
if (!firstLoadDone) {
|
||||
setStoredValue(fromLocal());
|
||||
setFirstLoadDone(true);
|
||||
}
|
||||
}, [firstLoadDone, initialValue, key]); // Only run when first load is done, initialValue, or key change
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
if (!firstLoadDone) return;
|
||||
|
||||
try {
|
||||
if (typeof window !== 'undefined') {
|
||||
window.localStorage.setItem(key, JSON.stringify(storedValue));
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}, [storedValue, firstLoadDone, key]); // Update localStorage when storedValue changes
|
||||
|
||||
return [storedValue, setStoredValue];
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
import { useMemo } from "react"
|
||||
import { CameraIcon, FacebookIcon, GithubIcon, InstagramIcon, MessageCircleDashedIcon, SendIcon } from 'lucide-react';
|
||||
|
||||
export const usePlatformIcons = (size = "small") => {
|
||||
const className = size === "small" ? 'h-3 w-3' : 'h-5 w-5'
|
||||
const platformsIcons = useMemo(() => ({
|
||||
"facebook": {
|
||||
icon: <FacebookIcon className={className} />, color: "indigo"
|
||||
},
|
||||
"instagram": {
|
||||
icon: <InstagramIcon className={className} />, color: "plum"
|
||||
},
|
||||
"telegram": {
|
||||
icon: <SendIcon className={className} />, color: "cyan"
|
||||
},
|
||||
"signal": {
|
||||
icon: <MessageCircleDashedIcon className={className} />, color: "blue"
|
||||
},
|
||||
"snapchat": {
|
||||
icon: <CameraIcon className={className} />, color: "yellow"
|
||||
},
|
||||
"github": {
|
||||
icon: <GithubIcon className={className} />, color: "gray"
|
||||
},
|
||||
}), [])
|
||||
return platformsIcons
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
import { createBrowserClient } from '@supabase/ssr'
|
||||
|
||||
if (!process.env.NEXT_PUBLIC_SUPABASE_URL || !process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY) {
|
||||
throw new Error('Missing Supabase environment variables');
|
||||
}
|
||||
|
||||
export const supabase = createBrowserClient(process.env.NEXT_PUBLIC_SUPABASE_URL, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY)
|
||||
@@ -1,65 +0,0 @@
|
||||
import { createServerClient } from '@supabase/ssr'
|
||||
import { NextResponse, type NextRequest } from 'next/server'
|
||||
|
||||
export async function updateSession(request: NextRequest) {
|
||||
let supabaseResponse = NextResponse.next({
|
||||
request,
|
||||
})
|
||||
const supabase = createServerClient(
|
||||
process.env.NEXT_PUBLIC_DOCKER_SUPABASE_URL!,
|
||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
||||
{
|
||||
cookies: {
|
||||
getAll() {
|
||||
return request.cookies.getAll()
|
||||
},
|
||||
setAll(cookiesToSet) {
|
||||
cookiesToSet.forEach(({ name, value, options }) => request.cookies.set(name, value))
|
||||
supabaseResponse = NextResponse.next({
|
||||
request,
|
||||
})
|
||||
cookiesToSet.forEach(({ name, value, options }) =>
|
||||
supabaseResponse.cookies.set(name, value, options)
|
||||
)
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// Do not run code between createServerClient and
|
||||
// supabase.auth.getUser(). A simple mistake could make it very hard to debug
|
||||
// issues with users being randomly logged out.
|
||||
|
||||
// IMPORTANT: DO NOT REMOVE auth.getUser()
|
||||
|
||||
const {
|
||||
data: { user },
|
||||
} = await supabase.auth.getUser()
|
||||
|
||||
if (
|
||||
!user &&
|
||||
!request.nextUrl.pathname.startsWith('/login') &&
|
||||
!request.nextUrl.pathname.startsWith('/auth') &&
|
||||
!request.nextUrl.pathname.startsWith('/api')
|
||||
) {
|
||||
// no user, potentially respond by redirecting the user to the login page
|
||||
const url = request.nextUrl.clone()
|
||||
url.pathname = '/login'
|
||||
return NextResponse.redirect(url)
|
||||
}
|
||||
|
||||
// IMPORTANT: You *must* return the supabaseResponse object as it is.
|
||||
// If you're creating a new response object with NextResponse.next() make sure to:
|
||||
// 1. Pass the request in it, like so:
|
||||
// const myNewResponse = NextResponse.next({ request })
|
||||
// 2. Copy over the cookies, like so:
|
||||
// myNewResponse.cookies.setAll(supabaseResponse.cookies.getAll())
|
||||
// 3. Change the myNewResponse object to fit your needs, but avoid changing
|
||||
// the cookies!
|
||||
// 4. Finally:
|
||||
// return myNewResponse
|
||||
// If this is not done, you may be causing the browser and server to go out
|
||||
// of sync and terminate the user's session prematurely!
|
||||
|
||||
return supabaseResponse
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
import { createServerClient } from '@supabase/ssr'
|
||||
import { cookies } from 'next/headers'
|
||||
|
||||
export async function createClient() {
|
||||
const cookieStore = await cookies()
|
||||
const supabaseToken = cookieStore.get('sb-token')?.value
|
||||
|
||||
return createServerClient(
|
||||
process.env.NEXT_PUBLIC_DOCKER_SUPABASE_URL!,
|
||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
||||
{
|
||||
auth: {
|
||||
persistSession: false,
|
||||
// Add the token if it exists
|
||||
...(supabaseToken && {
|
||||
autoRefreshToken: false,
|
||||
detectSessionInUrl: false,
|
||||
storage: {
|
||||
getItem: () => supabaseToken,
|
||||
setItem: () => { },
|
||||
removeItem: () => { },
|
||||
},
|
||||
}),
|
||||
},
|
||||
cookies: {
|
||||
getAll() {
|
||||
return cookieStore.getAll()
|
||||
},
|
||||
setAll(cookiesToSet) {
|
||||
try {
|
||||
cookiesToSet.forEach(({ name, value, options }) =>
|
||||
cookieStore.set(name, value, options)
|
||||
)
|
||||
} catch {
|
||||
// The `setAll` method was called from a Server Component.
|
||||
// This can be ignored if you have middleware refreshing
|
||||
// user sessions.
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -1,260 +0,0 @@
|
||||
import { clsx, type ClassValue } from "clsx"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
//@ts-ignore
|
||||
import * as d3 from "d3-force"
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
}
|
||||
|
||||
export const zoomSelector = (s: { transform: number[]; }) => s.transform[2] >= 0.6;
|
||||
|
||||
import { Position, MarkerType } from '@xyflow/react';
|
||||
import { AppNode } from "@/store/flow-store";
|
||||
|
||||
// this helper function returns the intersection point
|
||||
// of the line between the center of the intersectionNode and the target node
|
||||
function getNodeIntersection(intersectionNode: { measured: { width: any; height: any; }; internals: { positionAbsolute: any; }; }, targetNode: { internals: { positionAbsolute: any; }; measured: { width: number; height: number; }; }) {
|
||||
// https://math.stackexchange.com/questions/1724792/an-algorithm-for-finding-the-intersection-point-between-a-center-of-vision-and-a
|
||||
const { width: intersectionNodeWidth, height: intersectionNodeHeight } =
|
||||
intersectionNode.measured;
|
||||
const intersectionNodePosition = intersectionNode.internals.positionAbsolute;
|
||||
const targetPosition = targetNode.internals.positionAbsolute;
|
||||
|
||||
const w = intersectionNodeWidth / 2;
|
||||
const h = intersectionNodeHeight / 2;
|
||||
|
||||
const x2 = intersectionNodePosition.x + w;
|
||||
const y2 = intersectionNodePosition.y + h;
|
||||
const x1 = targetPosition.x + targetNode.measured.width / 2;
|
||||
const y1 = targetPosition.y + targetNode.measured.height / 2;
|
||||
|
||||
const xx1 = (x1 - x2) / (2 * w) - (y1 - y2) / (2 * h);
|
||||
const yy1 = (x1 - x2) / (2 * w) + (y1 - y2) / (2 * h);
|
||||
const a = 1 / (Math.abs(xx1) + Math.abs(yy1));
|
||||
const xx3 = a * xx1;
|
||||
const yy3 = a * yy1;
|
||||
const x = w * (xx3 + yy3) + x2;
|
||||
const y = h * (-xx3 + yy3) + y2;
|
||||
|
||||
return { x, y };
|
||||
}
|
||||
|
||||
// returns the position (top,right,bottom or right) passed node compared to the intersection point
|
||||
function getEdgePosition(node: { internals: { positionAbsolute: any; }; }, intersectionPoint: { x: any; y: any; }) {
|
||||
const n = { ...node.internals.positionAbsolute, ...node };
|
||||
const nx = Math.round(n.x);
|
||||
const ny = Math.round(n.y);
|
||||
const px = Math.round(intersectionPoint.x);
|
||||
const py = Math.round(intersectionPoint.y);
|
||||
|
||||
if (px <= nx + 1) {
|
||||
return Position.Left;
|
||||
}
|
||||
if (px >= nx + n.measured.width - 1) {
|
||||
return Position.Right;
|
||||
}
|
||||
if (py <= ny + 1) {
|
||||
return Position.Top;
|
||||
}
|
||||
if (py >= n.y + n.measured.height - 1) {
|
||||
return Position.Bottom;
|
||||
}
|
||||
|
||||
return Position.Top;
|
||||
}
|
||||
|
||||
// returns the parameters (sx, sy, tx, ty, sourcePos, targetPos) you need to create an edge
|
||||
export function getEdgeParams(source: any, target: any) {
|
||||
const sourceIntersectionPoint = getNodeIntersection(source, target);
|
||||
const targetIntersectionPoint = getNodeIntersection(target, source);
|
||||
|
||||
const sourcePos = getEdgePosition(source, sourceIntersectionPoint);
|
||||
const targetPos = getEdgePosition(target, targetIntersectionPoint);
|
||||
|
||||
return {
|
||||
sx: sourceIntersectionPoint.x,
|
||||
sy: sourceIntersectionPoint.y,
|
||||
tx: targetIntersectionPoint.x,
|
||||
ty: targetIntersectionPoint.y,
|
||||
sourcePos,
|
||||
targetPos,
|
||||
};
|
||||
}
|
||||
|
||||
export function initialElements() {
|
||||
const nodes = [];
|
||||
const edges = [];
|
||||
const center = { x: window.innerWidth / 2, y: window.innerHeight / 2 };
|
||||
|
||||
nodes.push({ id: 'target', data: { label: 'Target' }, position: center });
|
||||
|
||||
for (let i = 0; i < 8; i++) {
|
||||
const degrees = i * (360 / 8);
|
||||
const radians = degrees * (Math.PI / 180);
|
||||
const x = 250 * Math.cos(radians) + center.x;
|
||||
const y = 250 * Math.sin(radians) + center.y;
|
||||
|
||||
nodes.push({ id: `${i}`, data: { label: 'Source' }, position: { x, y } });
|
||||
|
||||
edges.push({
|
||||
id: `edge-${i}`,
|
||||
target: 'target',
|
||||
source: `${i}`,
|
||||
type: 'floating',
|
||||
markerEnd: {
|
||||
type: MarkerType.Arrow,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return { nodes, edges };
|
||||
}
|
||||
|
||||
interface Node {
|
||||
id: string
|
||||
position?: { x: number; y: number }
|
||||
measured?: { width: number; height: number }
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
interface Edge {
|
||||
source: string
|
||||
target: string
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
interface LayoutOptions {
|
||||
direction?: string
|
||||
strength?: number
|
||||
distance?: number
|
||||
iterations?: number
|
||||
}
|
||||
|
||||
export const getLayoutedElements = (
|
||||
nodes: AppNode[],
|
||||
edges: Edge[],
|
||||
options: LayoutOptions = {
|
||||
direction: "LR",
|
||||
strength: -300,
|
||||
distance: 100,
|
||||
iterations: 300,
|
||||
},
|
||||
) => {
|
||||
// Create a map of node IDs to indices for the simulation
|
||||
const nodeMap = new Map(nodes.map((node, i) => [node.id, i]))
|
||||
|
||||
// Create a copy of nodes with positions for the simulation
|
||||
const nodesCopy = nodes.map((node) => ({
|
||||
...node,
|
||||
x: node.position?.x || Math.random() * 500,
|
||||
y: node.position?.y || Math.random() * 500,
|
||||
width: node.measured?.width || 0,
|
||||
height: node.measured?.height || 0,
|
||||
}))
|
||||
|
||||
// Create links for the simulation using indices
|
||||
const links = edges.map((edge) => ({
|
||||
source: nodeMap.get(edge.source),
|
||||
target: nodeMap.get(edge.target),
|
||||
original: edge,
|
||||
}))
|
||||
|
||||
// Create the simulation
|
||||
const simulation = d3
|
||||
.forceSimulation(nodesCopy)
|
||||
.force(
|
||||
"link",
|
||||
d3.forceLink(links).id((d: any) => nodeMap.get(d.id)),
|
||||
)
|
||||
.force("charge", d3.forceManyBody().strength(options.strength || -300))
|
||||
.force("center", d3.forceCenter(250, 250))
|
||||
.force(
|
||||
"collision",
|
||||
d3.forceCollide().radius((d: any) => Math.max(d.width, d.height) / 2 + 10),
|
||||
)
|
||||
|
||||
// If direction is horizontal, adjust forces
|
||||
if (options.direction === "LR") {
|
||||
simulation.force("x", d3.forceX(250).strength(0.1))
|
||||
simulation.force("y", d3.forceY(250).strength(0.05))
|
||||
} else {
|
||||
simulation.force("x", d3.forceX(250).strength(0.05))
|
||||
simulation.force("y", d3.forceY(250).strength(0.1))
|
||||
}
|
||||
|
||||
// Run the simulation synchronously
|
||||
simulation.stop()
|
||||
for (let i = 0; i < (options.iterations || 300); i++) {
|
||||
simulation.tick()
|
||||
}
|
||||
|
||||
// Update node positions based on simulation results
|
||||
const updatedNodes = nodesCopy.map((node) => ({
|
||||
...node,
|
||||
position: {
|
||||
x: node.x - node.width / 2,
|
||||
y: node.y - node.height / 2,
|
||||
},
|
||||
}))
|
||||
|
||||
return {
|
||||
nodes: updatedNodes,
|
||||
edges,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export const sanitize = (name: string) => {
|
||||
return name
|
||||
.normalize("NFKD") // Decompose special characters (e.g., accents)
|
||||
.replace(/[\u0300-\u036f]/g, "") // Remove accent marks
|
||||
.replace(/[^a-zA-Z0-9.\-_]/g, "_") // Replace invalid characters with underscores
|
||||
.replace(/_{2,}/g, "_") // Replace multiple underscores with a single one
|
||||
.replace(/^_|_$/g, "") // Trim leading or trailing underscores
|
||||
.toLowerCase(); // Convert to lowercase for consistency
|
||||
};
|
||||
|
||||
export const formatFileSize = (bytes: number) => {
|
||||
if (bytes < 1024) return bytes + " B"
|
||||
else if (bytes < 1048576) return (bytes / 1024).toFixed(1) + " KB"
|
||||
else return (bytes / 1048576).toFixed(1) + " MB"
|
||||
}
|
||||
|
||||
export const nodesTypes = {
|
||||
emails: { table: "emails", type: "email", fields: ["email"] },
|
||||
individuals: { table: "individuals", type: "individual", fields: ["full_name"] },
|
||||
phone_numbers: { table: "phone_numbers", type: "phone", fields: ["phone_number"] },
|
||||
ip_addresses: { table: "ip_addresses", type: "ip", fields: ["ip_address"] },
|
||||
social_accounts_facebook: {
|
||||
table: "social_accounts",
|
||||
type: "social",
|
||||
fields: ["profile_url", "username", "platform:facebook"],
|
||||
},
|
||||
social_accounts_instagram: {
|
||||
table: "social_accounts",
|
||||
type: "social",
|
||||
fields: ["profile_url", "username", "platform:instagram"],
|
||||
},
|
||||
social_accounts_telegram: {
|
||||
table: "social_accounts",
|
||||
type: "social",
|
||||
fields: ["profile_url", "username", "platform:telegram"],
|
||||
},
|
||||
social_accounts_snapchat: {
|
||||
table: "social_accounts",
|
||||
type: "social",
|
||||
fields: ["profile_url", "username", "platform:snapchat"],
|
||||
},
|
||||
social_accounts_signal: {
|
||||
table: "social_accounts",
|
||||
type: "social",
|
||||
fields: ["profile_url", "username", "platform:signal"],
|
||||
},
|
||||
social_accounts_github: {
|
||||
table: "social_accounts",
|
||||
type: "social",
|
||||
fields: ["profile_url", "username", "platform:github"],
|
||||
},
|
||||
physical_addresses: { table: "physical_addresses", type: "address", fields: ["address", "city", "country", "zip"] },
|
||||
}
|
||||
Reference in New Issue
Block a user