Redirecting (wp safe redirect) with delay for clientdata to be collected

I’ve written a custom script that generates a fingerprint based on both server-side and client-side data to block users who keep coming back with different IP addresses. I know it’s not 100% foolproof, but it’s a last-resort failsafe that is currently working smoothly.

The issue I’m encountering occurs when a page is first loaded in a new session. The PHP code is executed before the JavaScript code has a chance to collect the client-side data. Specifically, the fingerprint function is called, but since there is no fingerprint in the session at that point, generate_fingerprint() is triggered. However, when the client-side data is collected, there is no POST data available (because the JavaScript hasn’t run yet), causing the function to return null. This causes the blocking mechanism to fail in this scenario.

This is the code piece I currently have (in combination with a javascript file) to generate the client-data based information for the fingerprint:

function generate_advanced_fingerprint() {
    $components = [
        'screen_resolution' => $_POST['screen_resolution'] ?? '',
        'color_depth' => $_POST['color_depth'] ?? '',
        'canvas_fingerprint' => $_POST['canvas_fingerprint'] ?? '',
        'webgl_fingerprint' => $_POST['webgl_fingerprint'] ?? '',
        'audio_fingerprint' => $_POST['audio_fingerprint'] ?? '',
        'installed_fonts' => $_POST['installed_fonts'] ?? '',
        'hardware_concurrency' => $_POST['hardware_concurrency'] ?? '',
        'device_memory' => $_POST['device_memory'] ?? '',
        'browser_features' => $_POST['browser_features'] ?? ''
    ];

    if (in_array('', $components, true)) {
        return null;
    }
    ksort($components);
    $structured_data = json_encode($components);
    $fingerprint = md5($structured_data);
    return $fingerprint;
}

Note: The javascript is execute in the top of the <head> section

This is the code I use to generate the fingerprint and do the block check against the blocklist (a JSON file):

function handle_fingerprint() {
    if (!is_page('test')) {
        return;
    }

    $is_post_request = ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_SERVER['HTTP_X_FINGERPRINT_REQUEST']));

    if ($is_post_request) {
        // Process the POST data with client-side information
        $raw_post_data = file_get_contents('php://input');
        $json_data = json_decode($raw_post_data, true);
        if ($json_data) {
            $_POST = array_merge($_POST, $json_data);
        }
    }

    // Generate or retrieve the fingerprint
    if (!isset($_SESSION['fingerprint_generated']) || $is_post_request) {
        $fingerprint = generate_fingerprint();
        $_SESSION['fingerprint_generated'] = true;
        $_SESSION['current_fingerprint'] = $fingerprint;

        // Save the fingerprint if it is a POST request
        if ($is_post_request) {
            $ip = get_visitor_ip();
            save_fingerprint($ip, $fingerprint);
            
            // Send a response to the POST request
            header('Content-Type: application/json');
            echo json_encode(['success' => true]);
            exit;
        }
    } else {
        $fingerprint = $_SESSION['current_fingerprint'];
    }

    // Run the check
    $blocked_fingerprints = get_blocked_fingerprints();

    // Check fallback fingerprint
    if (in_array($fingerprint['fallback_fingerprint'], $blocked_fingerprints)) {
        wp_safe_redirect(home_url('/'));
        exit;
    }

    // Check advanced fingerprint
    if (isset($fingerprint['advanced_fingerprint']) && in_array($fingerprint['advanced_fingerprint'], $blocked_fingerprints)) {
        wp_safe_redirect(home_url('/'));
        exit;
    }

}

// Check the fingerprint on every pageload
add_action('wp', 'handle_fingerprint');

// Process POST requests for fingerprint data
add_action('init', function() {
    if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_SERVER['HTTP_X_FINGERPRINT_REQUEST'])) {
        handle_fingerprint();
    }
});

In this scenario (with a new session), after the page loads, the JavaScript executes window.getFingerprint(). This function collects client-side data and sends it to the server via a POST request. This triggers the handle_fingerprint() function again. Now the POST data is available, so generate_advanced_fingerprint() can execute successfully. A complete fingerprint is generated.

The result is a double call (which I can make visible via my error logs).

I’ve tried generating the fingerprint only once all the data is available:

    $is_post_request = ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_SERVER['HTTP_X_FINGERPRINT_REQUEST']));

    if ($is_post_request) {
        // Process the POST data with client-side information
        $raw_post_data = file_get_contents('php://input');
        $json_data = json_decode($raw_post_data, true);
        if ($json_data) {
            $_POST = array_merge($_POST, $json_data);
        }
    }

Which works, but by the time the clientdata is available, the content has already been served, and the redirect mechanism doesn’t work anymore. Now, I’m trying to find a way to delay the blocking check until the client-side data is available, but so far, I haven’t been successful.

Note:

  • I want to avoid showing error messages. I rather have them redirected to a “faux” page designed to distract rather than making them any wiser than they are.

  • I also want to avoid using javascript for the redirect. I would like to stay with the wp_safe_redirect() or something likewise if possible.

Can anyone help me out with a fresh look at it?

Update with C# and React [closed]

I need to update my database using C# in the back-end and React in front-end.

Back-end

[HttpPost("update")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<Resultado<Analise>> AnaliseAsync([FromBody] Analise analise)

{
    _telemetria.AdicionarPropriedade("NomeUsuario", _login);
    _telemetria.EscreverTelemetria(GetType().Name, "Atualizar análise", TipoSeveridade.Informacao);

    if (analise == null)
    {
        return Resultado<Analise>.AdicionarErro(
          null,
          CreditOn.Dominio.Enums.StatusCode.Status400BadRequest,
          "Dados inválidos."
        );
    }

    var resultado = await _servicoAnalise.AtualizarAnalise(analise.AnaliseId, _login);

    if (resultado.Erro)
    {
        return Resultado<Analise>.AdicionarErro(
          null,
          resultado.StatusCode,
          resultado.Erros
        );
    }

    _telemetria.EscreverTelemetria(
      "AtualizarAnálise",
      "Análise atualizada com sucesso.",
      TipoSeveridade.Informacao,
      propriedades: new Dictionary<string, object> { { "ID_Analise", analise.AnaliseId } }
    );
    return Resultado<Analise>.AdicionarSucesso(analise);
}```


**Front-end**


interface EditableTableProps {
    resultado: Resultado[];
    onUpdate: (updatedData: Resultado[]) => void;
}


const EditableTable = ({ resultado, onUpdate }: EditableTableProps) => {
    const [data, setData] = useState<Resultado[]>(Array.isArray(resultado) ? resultado : []);
    const [loading, setLoading] = useState(false);
    const [error, setError] = useState<string | null>(null);
    

    console.log('Análise:', data)
    

    const handleInputChange = (
        e: React.ChangeEvent<HTMLInputElement>,
        field: string,
        itemId: string
    ) => {
        const updatedData = data.map((analise) => ({
            ...analise,
            analiseDemonstrativo: analise.analiseDemonstrativo?.map((item) =>
                item.analiseDemonstrativoId === itemId
                    ? { ...item, [field]: e.target.value }
                    : item
            ) || [],
        }));
    
        setData(updatedData);
        onUpdate(updatedData);
    };
    console.log('resultado:', resultado)
    console.log('result:', result)
    const handleSave = async () => {
        setLoading(true);
        setError(null);
    
        try {
            const payload = {
                analiseId: analiseId, // Adicionar o analiseId aqui
                analises: data.map((analise) => ({
                    analiseId: analise.analiseId,
                    analiseDemonstrativo: analise.analiseDemonstrativo?.map((item) => ({
                        analiseDemonstrativoId: item.analiseDemonstrativoId,
                        tipoParametro: item.tipoParametro,
                        valor: item.valor,
                        // Adicione outros campos necessários aqui
                    })) || [],
                })),
            };
            
            console.log('payload:', JSON.stringify(payload, null, 2));
    
            const response = await axios.post(
                'https://localhost:7116/analise/update',
                payload,
                { headers: { 
                    'Authorization': `Bearer ${token}`,
                    'Content-Type': 'application/json' 
                } }
            );
    
            console.log('Dados enviados com sucesso:', response.data);
            setResult(response.data.valor || []);
            alert('Dados atualizados com sucesso!');
        } catch (err) {
            console.error('Erro ao atualizar dados:', err);
            setError('Ocorreu um erro ao enviar os dados. Verifique os valores e tente novamente.');
            
        } finally {
            setLoading(false);
        }
    };
    
    return (
        <div>
            <table style={{ width: '100%', border: '1px solid black', borderCollapse: 'collapse' }}>
                <thead>
                    <tr>
                        <th style={{ border: '1px solid black', padding: '8px' }}>Tipo Parâmetro</th>
                        <th style={{ border: '1px solid black', padding: '8px' }}>Valor</th>
                    </tr>
                </thead>
                <tbody>
                {data.map((analise) =>
                    analise.analiseDemonstrativo?.map((item: any) => (
                        <tr key={item.analiseDemonstrativoId}>

                            <td style={{ border: '1px solid black', padding: '8px' }}>
                                <input
                                    type="number"
                                    value={item.tipoParametro}
                                    onChange={(e) => handleInputChange(e, 'tipoParametro', item.analiseDemonstrativoId)}
                                    style={{ width: '100%' }} />
                            </td>

                            <td style={{ border: '1px solid black', padding: '8px' }}>
                                <input
                                    type="number"
                                    value={item.valor}
                                    onChange={(e) => handleInputChange(e, 'valor', item.analiseDemonstrativoId)}
                                    style={{ width: '100%' }} />
                            </td>

                        </tr>
                    )) || []
                )}
                </tbody>
            </table>

            {/* Botão de envio */}
            <button
                onClick={handleSave}
                style={{
                    marginTop: '10px',
                    padding: '8px 16px',
                    backgroundColor: '#4CAF50',
                    color: 'white',
                    border: 'none',
                    cursor: 'pointer',
                }}
                disabled={loading}
            >
                {loading ? 'Salvando...' : 'Salvar Alterações'}
            </button>

            {/* Feedback de erro */}
            {error && <p style={{ color: 'red' }}>{error}</p>}
        </div>
    );
};

const TableAnalysis: React.FC<{ resultado: any[] }> = ({ resultado }) => {
    if (!resultado || resultado.length === 0) {
        return <p>Nenhum dado encontrado.</p>;
    }

    const handleUpdate = (updatedData: Resultado[]) => {
        // Atualize o estado ou faça algo com os dados atualizados
        console.log('Dados atualizados:', updatedData);
    };

    return <EditableTable resultado={resultado} onUpdate={handleUpdate} />;
};

    const handleUpdate = (updatedData: Resultado[]) => {
        setResult(updatedData);
    };

    if (!pdfUrl) {
        return <p>Carregando ou URL do PDF não encontrada.</p>;
    }

    return (
        <Container>
            <main>
                <div className="Container1">
                    <h1>Page</h1>
                    
                    <div className="row">
                        <div className="col1">
                            <h1>Visualização do PDF</h1>
                           <VisualizationPage pdfUrl={pdfUrl} />
                            <PdfButton url="https://extratordev.blob.core.windows.net/95e6b155-8152-4c3c-8430-bef3333be2aa/07486111-c9fe-44ce-a5df-707985b7d2f7-Balanco_Patrimonial_2022-CostaSul.pdf" label="Abrir PDF 1" onClick={setPdfUrl} />
                            <PdfButton url="https://cdn.filestackcontent.com/wcrjf9qPTCKXV3hMXDwK" label="Abrir PDF 2" onClick={setPdfUrl} />
                            <PdfButton url="./Costa_Sul.pdf" label="Abrir PDF 3" onClick={setPdfUrl} /> 
                        </div>
                        <div className="col2">
                            <h2>Indicadores</h2>
                            <div className="Indicators">
                                <h2>Análise Demonstrativa</h2>
                                <TableAnalysis
                                    resultado={(result || [])}
                                />

                            </div>
                            <MyButton />
                        </div>
                    </div>
                </div>
            </main>
        </Container>
    );
};

My payload in the console

payload: {
  "analiseId": "30d972df-9f8f-491c-8e****",
  "analises": [
    {
      "analiseId": "30d972df-9f8f-491***",
      "analiseDemonstrativo": [
        {
          "analiseDemonstrativoId": "45b93269-e007-4f23-a04***",
          "tipoParametro": "2",
          "valor": 30000
        }
      ]
    }
  ]
}

I got data updated with success:

{statusCode: 200, erro: false, erros: Array(0), valor: {…}}
erro
:
false
erros
:
[]
statusCode
:
200
valor
:
{analiseId: ’30d972df-9f8f-491c-8e76-fdae76eeac2c’, cnpjOuCpf: null, statusId: ‘00000000-0000-0000-0000-000000000000’, tipoAnalise: 0, observacao: null, …}
[[Prototype]]
:
Object

An the empty result like this:

analiseCliente
:
[]
analiseCredito
:
[]
analiseCriterio
:
[]
analiseDemonstrativo
:
[]
analiseDocumentacao
:
[]
analiseEmpresa
:
[]
analiseId
:
“30d972df-9f8f-491c-8e76-fdae76eeac2c”
analiseIndicador
:
[]
analiseMensagem
:
[]
analiseMetadado
:
[]
analisePorte
:
[]
analiseSociedade
:
[]
classificacaoInterna
:
null
cnpjMatriz
:
null
cnpjOuCpf
:
null
dataEdicao
:
“0001-01-01T00:00:00”
dataInclusao
:
“0001-01-01T00:00:00”
nomeAssessor
:
null
observacao
:
null
parecer
:
null
parecerComercial
:
null
status
:
null
statusId
:
“00000000-0000-0000-0000-000000000000”
tipoAnalise
:
0
usuarioEdicao
:
null
usuarioInclusao
:
null
valorSolicitado
:
0
[[Prototype]]
:
Object

How can I change my code to obtain simpler form and brighter blinking dots [closed]

I want to obtain blinking dots one after one in one direction and then blinking have to go back, and all have to happen in the loop. I have created CSS code with Javascript according to first five dots. What can I do to make shorter all reqired code in CSS? Also I want to make not visible black backgrounds of the dots during blinking.

for (let i = 0; i < 40; i++) {
  const dots = document.createElement("div");
  document.body.appendChild(dots).classList.add("dots");
}
body {
  background-color: #737373;
  display: flex;
  justify-content: space-evenly;
  align-items: center;
  min-height: 100vh;
  overflow: hidden;
}

.dots {
  background-color: rgb(0, 0, 0);
  width: 20px;
  height: 20px;
  border-radius: 50%;
}

.dots:nth-of-type(1)::after {
  content: "";
  position: absolute;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  animation: bright 0.3s linear;
  background-color: rgb(255, 255, 255);
  box-shadow: 0 0 3px 3px rgb(255, 255, 255),
    0 0 5px 5px rgb(255, 255, 255), 0 0 12px 12px rgb(255, 255, 255);
  opacity: 0;
  animation-delay: 0s;
}

.dots:nth-of-type(2)::after {
  content: "";
  position: absolute;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  animation: bright 0.3s linear;
  background-color: rgb(255, 255, 255);
  box-shadow: 0 0 3px 3px rgb(255, 255, 255),
    0 0 5px 5px rgb(255, 255, 255), 0 0 12px 12px rgb(255, 255, 255);
  opacity: 0;
  animation-delay: 0.25s;
}

.dots:nth-of-type(3)::after {
  content: "";
  position: absolute;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  animation: bright 0.3s linear;
  background-color: rgb(255, 255, 255);
  box-shadow: 0 0 3px 3px rgb(255, 255, 255),
    0 0 5px 5px rgb(255, 255, 255), 0 0 12px 12px rgb(255, 255, 255);
  opacity: 0;
  animation-delay: 0.5s;
}

.dots:nth-of-type(4)::after {
  content: "";
  position: absolute;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  animation: bright 0.3s linear;
  background-color: rgb(255, 255, 255);
  box-shadow: 0 0 3px 3px rgb(255, 255, 255),
    0 0 5px 5px rgb(255, 255, 255), 0 0 12px 12px rgb(255, 255, 255);
  opacity: 0;
  animation-delay: 0.75s;
}

.dots:nth-of-type(5)::after {
  content: "";
  position: absolute;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  animation: bright 0.3s linear;
  background-color: rgb(255, 255, 255);
  box-shadow: 0 0 3px 3px rgb(255, 255, 255),
    0 0 5px 5px rgb(255, 255, 255), 0 0 12px 12px rgb(255, 255, 255);
  opacity: 0;
  animation-delay: 1s;
}

@keyframes bright {

  0%,
  100% {
    opacity: 0;
  }

  50% {
    opacity: 1;
  }
}

How to retrieve automatic values from the Y-axis in the recharts library?

The domain parameter is responsible for generating values on the Y-axis. If expenses are added, the axis generates 5 points (tickCount={5}) from 0 to the "auto" value, which returns an automatically generated value from the library.

Here is my question: how can I retrieve the highest value among the generated values on the Y-axis?

<YAxis
   dataKey="totalExpenses"
   orientation="right"
   type="number"
   tickCount={5}
   fontSize={10}
   fontWeight={500}
   width={40}
   domain={noExpenses || noData ? [0, 120] : [0, "auto"]}
/>

Example Chart

What I need to achieve is, for example, as shown in the sample image, I need to retrieve the value 600, which is the highest value from the Y-axis that was automatically generated in the YAxis component.

Review box disappears when slider to the left is clicked

I am very new to JavaScript.

My review slider works on the button to the right but when left is clicked and try to get from the first to the left last slide the review box disappears

const slides = document.querySelectorAll('.slide')
const leftBtn = document.getElementById('left')
const rightBtn = document.getElementById('right')

let activeSlide = 0

rightBtn.addEventListener('click', () => {
  activeSlide++

  if (activeSlide > slides.length - 1) {
    activeSlide = 0
  }

  setActiveSlide()
})

leftBtn.addEventListener('click', () => {
  activeSlide--

  if (activeSlide > slides.length - 1) {
    activeSlide = slides.length - 1
  }

  setActiveSlide()
})

function setActiveSlide() {
  slides.forEach(slide => {
    slide.classList.remove('active')
    slide.classList.add('hide')
  })

  slides [activeSlide].classList.add('active')
  slides [activeSlide].classList.remove('hide')
}
<section class="section-recensies">
    <div class="recensies container">
        <h2>Recensies</h2>
        <div class="slider-container">

            <div class="recensie-box slide active">
                <p class="recensie-tekst">Recensie</p>
                <img src="{{ asset('images/logo/grace-business-group-lijn-met-kroon.png') }}"
                     alt="Logo Grace Business Group met lijn">
                <p>
                    <span>Naam</span>
                    <span>Bedrijf</span>
                </p>
            </div>

            <div class="recensie-box slide hide">
                <p class="recensie-tekst">Recensie</p>
                <img src="{{ asset('images/logo/grace-business-group-lijn-met-kroon.png') }}"
                     alt="Logo Grace Business Group met lijn">
                <p>
                    <span>Naam</span>
                    <span>Bedrijf</span>
                </p>
            </div>

            <div class="recensie-box slide hide">
                <p class="recensie-tekst">Recensie</p>
                <img src="{{ asset('images/logo/grace-business-group-lijn-met-kroon.png') }}"
                     alt="Logo Grace Business Group met lijn">
                <p>
                    <span>Naam</span>
                    <span>Bedrijf</span>
                </p>
            </div>

            <button class="recensie-button recensie-button-links" id="left"><</button>
            <button class="recensie-button recensie-button-rechts" id="right">></button>

        </div>
    </div>
</section>

I can’t see why it is not working because it does go to the left. When I click the right button the review box appears again.

Angular Jasmine tests that continue upon previous tests

I have a service called items here is the spec file:

import { TestBed } from '@angular/core/testing';

import { ItemsService } from './items.service';

describe('ItemsListService', () => {
  let service: ItemsService;

  beforeEach(() => {
    TestBed.configureTestingModule({});
    service = TestBed.inject(ItemsService);
  });

  it('should be created', () => {
    expect(service).toBeTruthy();
  });

  it('should insert new item', () => {
    const item = {
      id: '123',
      name: 'item',
      baseUnit: 'unit',
      brand: 'brand',
      price: 100,
      quantity: 10,
      supplier: 'supplier',
      cost: 50,
      imageUrl: 'url',
    };
    service.insertItem(item);
    const items = service.getItems();
    expect(items.length).toBeGreaterThan(0);
    expect(items).toContain(item);
  });
});

And here is another service called orders that depends on items service to create orders from items stored.

import { TestBed } from '@angular/core/testing';

import { OrdersService } from './orders.service';

describe('OrdersService', () => {
  let service: OrdersService;

  beforeEach(() => {
    TestBed.configureTestingModule({});
    service = TestBed.inject(OrdersService);
  });

  it('should be created', () => {
    expect(service).toBeTruthy();
  });

  it('should insert new order', async () => {
    const newOrder = await service.insertOrder(itemsService.getItems()[0]);
  });
});

So in order for orders service to create a new order it needs to access the item stored inside items service here service.insertOrder(itemsService.getItems()[0]).

How do I access the same instance of items service created in the spec file?

Syncing two folders

I created custom script to sync data from 2 folders in Google Drive.
To be blunt this is due to me emulating gameboy/gameboy advance on both android device and PC.
The emulator on PC works fine for both GB/GBA games and it stores save files in specified location (does not diffrentiate ROM type, stores and load all .sav files from set location)
But on android I have two emulators (one for GB one for GBA) and each of them have option of syncing with google cloud, but no custom selecting target folder (these are by default named after emulators)
I of course selected one of these folder as save file location for PC emmulator, but it lacks saves from other.
I created google apps script that triggers based on timer (for testing set once every 5 minutes)

function syncFolders() {
  const gbcFolderId = <link to GB folder>; 
  const gbaFolderId = <link to GBA folder>; 

  const gbcFolder = DriveApp.getFolderById(gbcFolderId);
  const gbaFolder = DriveApp.getFolderById(gbaFolderId);

  syncFolderFiles(gbcFolder, gbaFolder); // Sync from GBC to GBA
  syncFolderFiles(gbaFolder, gbcFolder); // Sync from GBA to GBC
}

function syncFolderFiles(sourceFolder, targetFolder) {
  const sourceFiles = sourceFolder.getFiles();
  const targetFilesMap = createFileMap(targetFolder);

  while (sourceFiles.hasNext()) {
    const sourceFile = sourceFiles.next();
    const sourceName = sourceFile.getName();
    const sourceDate = sourceFile.getLastUpdated();

    if (
      targetFilesMap[sourceName] &&
      targetFilesMap[sourceName].date >= sourceDate
    ) {
      continue; // Skip if target file is newer or the same
    }

    // Copy or update the file in the target folder
    if (targetFilesMap[sourceName]) {
      targetFilesMap[sourceName].file.setTrashed(true); // Trash old version
    }
    sourceFile.makeCopy(sourceName, targetFolder);
  }
}

function createFileMap(folder) {
  const fileMap = {};
  const files = folder.getFiles();

  while (files.hasNext()) {
    const file = files.next();
    fileMap[file.getName()] = {
      file: file,
      date: file.getLastUpdated(),
    };
  }

  return fileMap;
}

The issue is that even though this script indeed syncs folders with each other allowing me to have all actual saves in both folders. but some of them are appended with incremented number (1) in the name, file copy style. This also lead to having save file for one of games in literally 5000 copies overnight.

And since old files are set to be trashed many of saves were unusable before renaming and removing index from the name.

Any idea how to fix the script to not append name/ trim it after copying or any other way to make the script work as intended?

How to fix “paths[0]” argument must be of type string. Received an instance of Array” error when introducing Mustache partials with ExpressJS?

index.js logging the view paths to ensure partials are visable

import express from 'express';
import bodyParser from 'body-parser';
import path from 'path';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
import mustacheExpress from 'mustache-express';
import dotenv from 'dotenv';
import homeRouter from './home/router.js';
import jobsRouter from './jobs/router.js';
import errors from './errors/errors.js';

dotenv.config();

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const app = express();

// Middleware
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static(path.join(__dirname, '..', 'public')));

// Configure mustache
app.set('views', [
  path.join(__dirname, 'home'), // Home templates
  path.join(__dirname, 'jobs'), // Jobs templates
  path.join(__dirname, 'errors'), // Error templates
  path.join(__dirname, 'site/partial-views'),
]);
console.log('Views set to:', app.get('views'));

app.set('view engine', 'mustache');
app.engine('mustache', mustacheExpress());

// Routes
app.use('/', homeRouter);
app.use('/jobs', jobsRouter);

// Error handling middleware
app.use(errors.notFound);
app.use(errors.internalServerError);
app.use(errors.emailError);

export default app;

terminal with views log

[nodemon] starting `node ./app/server.js`
Views set to: [
  'C:\Users\Keegan\source\repos\my-website\app\home',
  'C:\Users\Keegan\source\repos\my-website\app\jobs',
  'C:\Users\Keegan\source\repos\my-website\app\errors',
  'C:\Users\Keegan\source\repos\my-website\app\site\partial-views'
]
Server running on port 3000
Connected to MySQL database

terminal with error

node:internal/errors:540
      throw error;
      ^

TypeError [ERR_INVALID_ARG_TYPE]: The "paths[0]" argument must be of type string. Received an instance of Array
    at Object.resolve (node:path:198:9)
    at C:UsersKeegansourcereposmy-websitenode_modulesmustache-expressmustache-express.js:99:24
    at C:UsersKeegansourcereposmy-websitenode_modulesasyncdistasync.js:247:13
    at eachOfArrayLike (C:UsersKeegansourcereposmy-websitenode_modulesasyncdistasync.js:507:13)
    at eachOf (C:UsersKeegansourcereposmy-websitenode_modulesasyncdistasync.js:627:16)
    at awaitable (C:UsersKeegansourcereposmy-websitenode_modulesasyncdistasync.js:212:32)
    at _asyncMap (C:UsersKeegansourcereposmy-websitenode_modulesasyncdistasync.js:245:16)
    at Object.map (C:UsersKeegansourcereposmy-websitenode_modulesasyncdistasync.js:750:16)
    at Object.awaitable [as map] (C:UsersKeegansourcereposmy-websitenode_modulesasyncdistasync.js:212:32)
    at loadAllPartials (C:UsersKeegansourcereposmy-websitenode_modulesmustache-expressmustache-express.js:94:8) {
  code: 'ERR_INVALID_ARG_TYPE'
}

Node.js v22.12.0
[nodemon] app crashed - waiting for file changes before starting...

directory

my-website/
----app/
--------index.js
--------server.js
--------db.js
--------config.js
--------jobs/router.js, jobs.mustache
--------home/router.js, home.mustache
--------errors/errors.js, erorrs.mustache
--------site/partial-views/head.mustache
----public/

home.mustache

<html lang="en" data-bs-theme="auto">
{{> head}}
</html>

home/router.js

import express from 'express';
import connection from '../db.js';

const router = express.Router();

router.get('/', (req, res, next) => {
  const query = 'SELECT id, title, location, salary, posted FROM jobs';

  connection.query(query, (err, results) => {
    if (err) {
      return next(err);
    }
    res.render('home', { jobs: results });
  });
});

export default router;

home.mustache is currently the only template attempting to use the partial. Removing the 1 line {{> head}} in home.mustache and manually copying the contents of head.mustache in its place removes the error, and thewebsite renders the ‘/’ root endpoint correctly.

Thank you for your time and feedback !

Auth.js/NextAuth – Application redirect not working after successful sign in

I am using Auth.js Google Oauth to login the user, then checking if that email exists in my database. If yes, then I route the user to “/dashboard/inventory”. Once sign in is successful, my app is not auto-routing the user to “/dashboard/inventory”. It’s stuck on “/”. I am able to manually go to “/dashboard/inventory” and verify the authentication is successful.

If the user signs out, then the app properly redirects to “/” as I have setup, but that’s probably because I also have a middleware.js in place which is set to redirect to “/” if no user session is found.

app/auth.js

import NextAuth from "next-auth"
import Google from "next-auth/providers/google"
import { supabase } from "@/lib/supabase"

export const { handlers, auth, signIn, signOut } = NextAuth({
    providers: [Google],
    callbacks: {
        async signIn({ user }) {
            const { data, error } = await supabase
                .from('stores')
                .select()
                .eq('email', user.email)
                .single()

            if (error || !data) {
                return '/error'
            }

            return true
        },
    },
    pages: {
        error: '/error'
    }
})

app/utils/auth-utils.js

"use server"
import { signIn, signOut } from '@/auth'

const SignInWithGoogle = async () => {
    await signIn('google', { callbackUrl: "/dashboard/inventory" })
}

const SignOut = async () => {
    await signOut({ callbackUrl: "/" })
}

export { SignInWithGoogle, SignOut }

app/components/AuthComponent.js

"use client"
import { useSearchParams } from "next/navigation"
import { Button } from "@/components/ui/button"
import { SignInWithGoogle, SignOut } from "@/lib/auth-utils"

const AuthComponent = () => {
    const searchParams = useSearchParams()
    const error = searchParams?.get('error')

    return (
        <div className="flex flex-col w-full h-full justify-center items-center">{error && (
            <p className="text-red-500 mb-4">
                {error}
            </p>
        )}
            <form action={SignInWithGoogle}>
                <Button className="gap-2 font-semibold m-4" variant="secondary">
                    sign in with google
                </Button>
            </form>
            <form action={SignOut}>
            <Button type="submit" variant="secondary">sign out</Button>
            </form>
            </div>
    )
}

export default AuthComponent

app/page.js

import AuthComponent from "@/components/app/AuthComponent"

const HomePage = () => {
    

    return (
        <div className="flex w-full justify-center items-center overflow-hidden flex-col h-dvh p-4">
            <AuthComponent/>
        </div>
    )
}

export default HomePage

string length issue when sent to Laravel server

When a string is appended to a FormData object and sent to Laravel server, who decides the encoding that will be used? How can we make sure that the string length reported on the client-side matches the one that we get on the server, and not changed due to control characters, especially new line (rn vs ‘n`)?

Detail

I’m POSTing my model object that includes, among other things, its description that is a string (max: 1000 chars), using axios from my SPA (Vue 3 + Vuetify + TS) to the backend Laravel server’s API endpoint. Validations are in place on both client and server sides. On that frontend, this description is being displayed in a v-textarea that reports that the content is exactly 1000 characters, including 8 newline characters. This description, along with other data (including images), is then POSTed to the server using a FormData object.

On the server-side, this string is received as 1008 characters long instead of 1000, which causes the validation rule to fail. Upon inspecting the client- and server-side versions of the string, the only difference is that those newlines have been converted from n to rn at some point. I’m just looking for how to avoid this conversion or at least make the reported length match on both client and server sides.

Using igGrid to generate a horizontal grid based on columns

I am trying to get igGrid to generate a grid which would look something like this:

 |       | 2005/06 | 2006/07 | 2007/08 | 2008/09 | 2009/10 | 2010/11 | 2011/12 | 2023/13
 |ID     | 169530  | 169531  | 169532  | 169533  | 169534  | 169535  | 169536  | 169537
 |Land   | 0       | 1       | 2       | 3       | 4       | 5       | 6       | 7
 |Total  | 100     | 101     | 102     | 103     | 104     | 105     | 106     | 107

I have 2 arrays, columns and countyPivot which contain the data needed to generate the igGrid.

The array called columns has this data:

 columns[0] = {dataType: "string", headerText: "2005/06", hidden: false, key: "2005/06", width: "75px"}
 columns[1] = {dataType: "string", headerText: "2006/07", hidden: false, key: "2006/07", width: "75px"}
 columns[2] = {dataType: "string", headerText: "2007/08", hidden: false, key: "2007/08", width: "75px"}
 columns[3] = {dataType: "string", headerText: "2008/09", hidden: false, key: "2008/09", width: "75px"}
 columns[4] = {dataType: "string", headerText: "2009/10", hidden: false, key: "2009/10", width: "75px"}
 columns[5] = {dataType: "string", headerText: "2010/11", hidden: false, key: "2010/11", width: "75px"}
 columns[6] = {dataType: "string", headerText: "2011/12", hidden: false, key: "2011/12", width: "75px"}
 columns[7] = {dataType: "string", headerText: "2012/13", hidden: false, key: "2012/13", width: "75px"}

And the array called countyPivot has this data:

 countyPivot[0] = {{Key: "id", Value: 169530}, {Key: "TaxYear", Value: "2005/06"}, {Key: "Land", Value: 0}, {Key: "Total", Value: 100}}
 countyPivot[1] = {{Key: "id", Value: 169531}, {Key: "TaxYear", Value: "2006/07"}, {Key: "Land", Value: 1}, {Key: "Total", Value: 101}}
 countyPivot[2] = {{Key: "id", Value: 169532}, {Key: "TaxYear", Value: "2007/08"}, {Key: "Land", Value: 2}, {Key: "Total", Value: 102}}
 countyPivot[3] = {{Key: "id", Value: 169533}, {Key: "TaxYear", Value: "2008/09"}, {Key: "Land", Value: 3}, {Key: "Total", Value: 103}}
 countyPivot[4] = {{Key: "id", Value: 169534}, {Key: "TaxYear", Value: "2009/10"}, {Key: "Land", Value: 4}, {Key: "Total", Value: 104}}
 countyPivot[5] = {{Key: "id", Value: 169535}, {Key: "TaxYear", Value: "2010/11"}, {Key: "Land", Value: 5}, {Key: "Total", Value: 105}}
 countyPivot[6] = {{Key: "id", Value: 169536}, {Key: "TaxYear", Value: "2011/12"}, {Key: "Land", Value: 6}, {Key: "Total", Value: 106}}
 countyPivot[7] = {{Key: "id", Value: 169537}, {Key: "TaxYear", Value: "2012/13"}, {Key: "Land", Value: 7}, {Key: "Total", Value: 107}}

This is the definition of my igGrid:

 $("#grdCountyAssessment").igGrid({
      width: "100%",
      height: "500px",  // Adjust height as needed
      columns: columns,
      dataSource: countyPivot,   
      autoGenerateColumns: false, // We're manually defining the columns
      features: [
      {
           name: "Sorting"
      },
      {
           name: "Paging",       // Enable paging
           pageSize: 10          // Set page size
      }
      ]
 });

And this is what ends up getting generated:

enter image description here

How do I adjust the data source so the igGrid can display the data correctly?

Any assistance is greatly appreciated.

Javascript IF multiple arguments [duplicate]

I’m debugging an external js library code and I found this syntax:

if(argument1, argument2) { ... }

It is the first time I face more than one argument in an if block, what is its meaning?

I tried to understand how it works with some tests:

if(false,false,true) alert("TEST"); //this alert is fired!

and

if(true,false,false) alert("TEST"); //this alert is not fired!

And it seems only the last argument is evaluated.

Webauthn – Browser fails to recognize autocomplete attribute

I am using an input for users to log in using email or phone. However, recently added an initial support for passkeys. I want to use the Conditional UI so users are not bothered if no passkeys are stored for this site. To use that, I need an input with autocomplete attribute set to webauthn – as required for the browserautofill property to use conditional UI.

However, the browser -I believe- does not recognize the webauthn autocomplete value having 3 autocomplete values in total

Like this:

<input type="text" autocomplete="email tel-national webauthn" />

It works with autocomplete="email webauthn" or autocomplete="tel-national webauthn".

It only happens for my desktop browsers (Arc and Google Chrome)

Arc
Chromium Engine Version 131.0.6778.140


However, it works on Chrome mobile, the browser recognize the input and presents my passkey options

Tried with only 2 values.

I want to keep the email tel-national attributes as there are more users enrolled with them than passkey.

I would rather display the password selection modal than the dropdown of user autofill, but only the later supports Conditional UI and, thus, I use the ONLY input I have on this page.

The desired behavior would be to still let the users be suggested with emails and phones and only be suggested with passkey options if existing credentials (Conditional UI). Not sure if I remove email or tel-national I could still suggest both for users without passkeys.

how convert to array properly with special character in string [duplicate]

i want to split string to array,but if the string contain é,the split method not work

var string = "JonBenét";
var arrays = string.split("");

why arrays like this ?

Array(9) [ “J”, “o”, “n”, “B”, “e”, “n”, “e”, “́”, “t” ]

i want convert like this,any suggestion thanks very much

Array(8) [ “J”, “o”, “n”, “B”, “e”, “n”, “é”, “t” ]

enter image description here

i try Array.string(string),but it still convert
Array(9) [ “J”, “o”, “n”, “B”, “e”, “n”, “e”, “́”, “t” ]
enter image description here

Order of await execution makes no sense in node

I wanted to understand at which point an async js function was appended to the event loop and when it was being immediately executed:

async function test2(){
    console.log('3 test2 is being executed');
}
async function test(){
    console.log('2 test is being executed');

    await test2();
    console.log('4 test 2 was awaited');
}

console.log('1 test is about to be called');
test();
console.log('5 test was called');

Originally, I assumed that no matter if a function had any actual async functions happneing (setTimeout, fetch, loadFile, etc), the function would always be appended to the event loop if it was declared async.

So by that logic expected the order of console logs like this:

1 test is about to be called
5 test was called
2 test is being executed
3 test2 is being executed
4 test 2 was awaited

So I assumed, async function that don’t have any actual real async functions in them always get executed immediately, leading to this order:

1 test is about to be called
2 test is being executed
3 test2 is being executed
4 test 2 was awaited
5 test was called

But instead I get this:

1 test is about to be called
2 test is being executed
3 test2 is being executed
5 test was called
4 test 2 was awaited

This means the order of execution is:

test is called
test is being immediately executed (despite being marked async)
test2 is being immediately executed (same as test)
after test2 is being run, control is returned to line 14 (which means the event loop must have done something after all)
finally, test reports that test2 was executed.

Can somebody explain this to me? How can it be that async functions sometimes get thrown onto the event loop and sometimes not? I this predictable and when?

Also if you remove the await, the order is 1 2 3 4 5.

I’d just like to understand what’s the logic here.