Is it possible to upload a .csv file and create collection entries out of it?

I am trying to make new collection entries (the collection is already made) from an uploaded .csv file. Is this possible to be done only in strapi?

Till now I have made a collection:

{
  "kind": "collectionType",
  "collectionName": "locations",
  "info": {
    "singularName": "location",
    "pluralName": "locations",
    "displayName": "location",
    "description": ""
  },
  "options": {
    "draftAndPublish": true
  },
  "pluginOptions": {},
  "attributes": {
    "address": {
      "type": "text",
      "required": true
    },
    "country": {
      "type": "string",
      "required": true
    },
    "city": {
      "type": "string",
      "required": true
    },
    "zip": {
      "type": "string",
      "required": true
    }
  }
}

and a single-type upload component, where I only allow to upload a .csv file.

{
  "kind": "singleType",
  "collectionName": "loactions-uploads",
  "info": {
    "singularName": "loactions-upload",
    "pluralName": "loactions-uploads",
    "displayName": "Loactions-upload"
  },
  "options": {
    "draftAndPublish": true
  },
  "pluginOptions": {},
  "attributes": {
    "upload": {
      "allowedTypes": [
        "files"
      ],
      "type": "media",
      "multiple": false
    }
  }
}

and the lifecycles.js hook looks as:

const fs = require("fs");
const path = require("path");
const { parse } = require("csv-parse");

module.exports = {
  async afterCreate(event) {
    const { result } = event;
    const file = result.file;
    if (!file || file.ext !== ".csv") {
      strapi.log.warn("No valid .csv file found.");
      return;
    }

    const filePath = path.join(strapi.dirs.static.public, file.url);

    try {
      const data = fs.readFileSync(filePath, "utf-8");

      // Parse CSV
      const records = [];
      const parser = parse({ delimiter: ";", columns: true });
      parser.write(data);
      parser.end();

      parser.on("readable", () => {
        let record;
        while ((record = parser.read())) {
          records.push(record);
        }
      });

      await new Promise((resolve, reject) => {
        parser.on("end", resolve);
        parser.on("error", reject);
      });

      // Process records and create entries
      for (const record of records) {
        await strapi.entityService.create("api::location.location", {
          data: {
            zip: record.ZIP,
            address: record.Address,
            country: record.Country,
            city: record.city,
          },
        });
      }

      strapi.log.info(
        `Successfully imported ${records.length} entries from CSV.`
      );
    } catch (err) {
      strapi.log.error("Failed to process the CSV file:", err);
    }
  },
};

when I upload the file, nothing happens. I see no new entries. Why?

The .csv looks as follows:

ZIP;Address;City;Country;
1010;Seilerstätte 12/1;Vienna;Austria;
1030;Rochusgasse 9/21,Vienna;Austria;

Unable to click on date range text box using python and selenium

`

                                <div role="tabpanel" class="col-md-8 form-group" id="tooltip-hide">
                                    <div class="input-group">
                                        <span class="input-group-addon"><label style="margin:0px;padding:0px;">Dates</label></span>
                                        <input required="" type="text" name="report_range" id="repDateRange" placeholder="Select date range" class="form-control input-sm required" value="" readonly="" style="cursor:text; background:#fff;">
                                    </div>
                                    <div id="dateRangeError"></div>
                                </div>

            <input type="hidden" name="date_range_type" value="">

            <input type="hidden" name="start_date" value="">

            <input type="hidden" name="end_date" value="">

        </div>
    </div>`

Using all the locators including absolute Xpath, I am unable to click on date text box. I have also tried using TABS, javascript click and Actions.

Using all the locators including absolute Xpath, I am unable to click on date text box. I have also tried using TABS, javascript click and Actions.

Page source

code steps

what we need to click

How to use strapi’s log in puppeteer?

I want to use strapi’s log in puppeteer.

The strapi configuration is complete, and it is used well in the strapi internal code.

But even though I passed the log function to puppeteer, it does not work.
The output is printed to the console, but the message inside puppeteer's evaluate is not visible.

What is the problem?

The main functions and strapi settings are attached below.

// puppeteer code
export const test = async (log?) => {
  const cluster = getCluster();

  const jobData = {
    url: 'https://google.com',
  };

  const data = await cluster.execute(jobData,
    async ({ page, data }) => {
      const { url } = data;
      page.setDefaultNavigationTimeout(0);
      log && page.on('console', (msg) => log('[Log]:', msg.text()));
      await page.goto(url, { waitUntil: 'load' });

      const results = await page.evaluate(async () => {
        const tic = Date.now();
        try {
        } catch (err) {
          console.error('Detail fetch error:', err);
        }

        // const data = [];
        // ...somethings

        console.log(`Elapsed time: ${Date.now() - tic}ms`);

        return data;
      }); // evaluate

      return results;
    }); // cluster.execute
  return data;
};
// call puppeteer function in cron tab
await test(strapi.log.debug.bind(strapi.log));
// result in console
strapi  |  debug: [Log]:
strapi  |  debug: [Log]:
strapi  |  debug: [Log]:

setting

// ./config/middleware.ts
export default [
  {
    name: 'strapi::logger',
    config: {
      level: 'debug',
      exposeInContext: true,
      requests: true,
    },
  },
...
// ./config/logger.ts
import { winston, formats } from '@strapi/logger';
const { prettyPrint } = formats;

export default {
  transports: [
    new winston.transports.Console({
      level: 'debug',
      format: winston.format.combine(
        winston.format.errors({ stack: true }),
        prettyPrint({ timestamps: 'YYYY-MM-DD hh:mm:ss.SSS' })
      ),
    }),
  ],
};
// puppeteer cluster settings
import { Cluster } from 'puppeteer-cluster';

let cluster: Cluster<any> | null = null;

export async function initCluster() {
  if (!cluster) {
    cluster = await Cluster.launch({
      concurrency: Cluster.CONCURRENCY_PAGE,
      maxConcurrency: 5,
      puppeteerOptions: {
        headless: true,
        executablePath: '/usr/bin/chromium-browser',
        args: [
          '--no-sandbox',
          '--disable-setuid-sandbox',
          '--disable-dev-shm-usage',
          '--disable-gpu',
        ],
        ignoreHTTPSErrors: true,
      },
      timeout: 60 * 1000,
      monitor: true,
    });

    // 에러 핸들러 등록(선택)
    cluster.on('taskerror', (err, data) => {
      console.error(`Error in task: ${err.message}`, data);
    });
  }
  return cluster;
}

export function getCluster(): Cluster<any> {
  if (!cluster) {
    throw new Error('Cluster is not initialized. Call initCluster() first.');
  }
  return cluster;
}

is my JavaScript is missing something(yaama)? [closed]

function validateForm(){
var studentname = document.forms[“frmstdreg”][“txtstdname”].value;

if(studentname == “”){
document.getElementById(“errfullname”).innerHTML = “Fullname cannot be empty”;
document.forms[“frmstdreg”][“txtstdname”].style.border = “2px solid red”;
return false;
}

var studentaddress = document.getElementById(“address”).value;

if(studentaddress == “”){
alert(“Student Address cannot be empty”);
return false;
}
}

function addRandomWord(){
document.forms[“frmstdreg”][“txtstdname”].value = “Kishore S”;
}

</head>
<body>

    <div class="container">
        <div class="panel panel-primary">
            <div class="panel-heading">
                <h1>ITRC</h1>
            </div>
            <div class="panel-body">
                <h2>Student Registration</h2>
                <form name="frmstdreg" onsubmit="return validateForm()">
                    <div class="row">
                        <div class="col-xs-12 col-sm-6">
                            <div class="form-group">
                                <label>Full Name : </label>
                                <input type="text" name="txtstdname" placeholder="Enter your name here" class="form-control" />
                                <p id="errfullname" style="color: red;"></p>
                            </div>
                        </div>
                        <div class="col-xs-12 col-sm-6">
                            <div class="form-group">
                                <label>Address : </label>
                                <input type="text" id="address" name="txtaddress" class="form-control" />
                            </div>
                        </div>
                    </div>
                    <div class="row">
                        <div class="col-xs-12 col-sm-6">
                            <div class="form-group">
                                <label>Email Address : </label>
                                <input type="email" name="txtstdemail" class="form-control" />
                            </div>
                        </div>
                        <div class="col-xs-12 col-sm-6">
                            <div class="form-group">
                                <label>NIC : </label>
                                <input type="text" name="txtnic" class="form-control" />
                            </div>
                        </div>
                    </div>
                
                    <div class="row">
                        <div class="col-xs-12 col-sm-6">
                            <div class="form-group">
                                <label>Date of Birth : </label>
                                <input type="date" name="txtdob" class="form-control" />
                            </div>
                        </div>
                        <div class="col-xs-12 col-sm-6">
                            <div class="form-group">
                                <label>Mobile No : </label>
                                <input type="tel" name="txtdtel" class="form-control" />
                            </div>
                        </div>
                        
                    </div>
                    
                    <div class="row">
                        <div class="col-xs-12 col-sm-6">
                            <button type="submit" class="btn btn-primary"><span class="glyphicon glyphicon-floppy-disk"> </span> Save </button>
                        </div>
                        <div class="col-xs-12 col-sm-6">
                            <button type="button" onclick="addRandomWord()" class="btn btn-info"> Sample name </button>
                        </div>
                        
                    </div>
                
                
                
            
            
                </form>

suggestions for the above code

Leaflet: I can add class, but I can’t add style to GeoJSON

I add a class to my geoJSON path with leaflet.

poly = new L.GeoJSON(polyArea[0], { 
    style:{
        weight:2, 
        opacity:1, 
        fillOpacity:0.5,
        className:"poly",
    } 
}).addTo(map)

The poly class is correctly added to the path element besides the default leaflet-interactive class.

<path class="poly leaflet-interactive" 
stroke="grey" 
stroke-opacity="1" 
stroke-width="2" 
stroke-linecap="round" 
stroke-linejoin="round" 
fill="grey" 
fill-opacity="0.5" 
fill-rule="evenodd" d="M332 ...(the whole path) 332z"></path>

However, when I want to add a style to the poly class, let’s say transition: all 500ms ease-in-out to make it smoothly transition. It doesn’t work.

But when I added the same style to :global(.leaflet-interactive), it works. It solves my problem. But I’m curious, why adding the style directly to the class doesn’t work?

How to extract a selected area from a 3D globe in Three.js and transfer it to a new canvas while maintaining zoom and pan effects?

I’ve created a 3D globe using Three.js, and I’m using a jpg file as the texture for the globe. I want to select a specific region on the globe by drawing a rectangle over it, and then transfer that selected area to a new canvas.

However, I’m encountering an issue. When I zoom in, zoom out, or move the camera (pan) around the globe, and then try to draw the rectangle, it looks like the rectangle is being drawn on the initial state of the globe, not accounting for the zoom and pan effects. Essentially, the rectangle appears as though it’s not applied to the current view, but to the original state of the globe.

Here’s what I need help with:

Drawing a rectangle on the 3D globe.
Extracting the area inside the rectangle from the globe texture.
Transferring the extracted area to a new canvas while taking zoom and pan into account.
Can anyone guide me on how to ensure the rectangle is drawn with the current view’s zoom and pan effects in mind? Any help or suggestions would be appreciated!

    const ctx = debugCanvas.getContext('2d');

    const imageWidth = img.width;
    const imageHeight = img.height;

    const pixelsPerLon = imageWidth / 360;
    const pixelsPerLat = imageHeight / 180;

    let x1 = (maxLon + 180) * pixelsPerLon;
    let y1 = (90 - minLat) * pixelsPerLat;
    const width = boxwidth
    const height = boxheight



    ctx.drawImage(
        img,    // jpg file
        x1, y1, 
        width, height,  
        0, 0,                
        width, height  
    );

How to Master API Handling?

I am trying to master API handling in web development but I am facing challenge in understanding and apply best practices effectively. Specifically I am encountering difficulties in:

Interpreting and utilizing API documentation.

Making different types of API request (GET, POST, PUT, DELETE).

Properly handling API responses and error.

Ensuring security while working with API.

Selecting and using the right libraries or tools for API handling.

I am trying to master API handling in web development I expecting a proper Guide for API handling that easily understandable. that framework I work on my university project Webhooks , Websockets , gRPC and Rest framework.

vue-cal tabindex keeps skipping dates

When moving from a ‘MONDAY’ back to a ‘FRIDAY’, if the ‘FRIDAY’ does not have any events, elements that should be focused by ‘tab’ is just skipped over.

Here is a picture:

VUE-CAL elements skipped

When ‘shift-tabbing’ (going backwards), icons like the ‘pencil’ and ‘cart’ and ‘time’ are being skipped completely for ‘FRIDAY’. When moving forward there is no problem. Anybody have any experience with this?

Thank you.

Selenium Unable to Interact with Taskpane Elements in Outlook Add-in

I’m using Selenium and Chrome WebDriver to automate UI testing for a taskpane-based Outlook add-in. While Selenium can interact with the main Outlook web app, it fails to locate or interact with elements inside the taskpane when opened. This issue prevents me from retrieving element properties (e.g., textContent, innerHTML) and running automated UI tests.

Environment

  • Platform: PC Desktop
  • Host: Outlook Web (OWA)
  • Office Version Number: N/A
  • Operating System: Windows 11
  • Browser: Chrome (latest stable version)

Expected Behavior

When using Selenium to automate UI testing for a taskpane-based Outlook add-in, I expect to:

  • Open the Outlook web app.
  • Launch the taskpane add-in.
  • Use JavaScript through Selenium to locate elements in the taskpane and retrieve their properties (e.g., textContent, innerHTML).

Current Behavior

Selenium is able to:

  • Launch the Outlook web app.
  • Navigate and interact with elements outside the taskpane. However:
  • When the taskpane is opened, Selenium is unable to locate or interact with the elements inside it using JavaScript.
  • Attempts to retrieve properties like textContent and innerHTML of taskpane elements fail or return null/undefined.

Steps to Reproduce

  1. Launch the Outlook web app using Selenium and Chrome WebDriver.
  2. Open the taskpane-based add-in.
  3. Use Selenium to execute JavaScript to locate elements in the taskpane iframe and retrieve their properties.
  4. Observe that element interactions or property retrieval do not work.

Context

This issue blocks automation of UI testing for the taskpane-based Outlook add-in, making it challenging to verify element properties and run UI automated tests. The inability to interact with taskpane elements via JavaScript affects:

  • UI validation during testing.

Module build failed: UnhandledSchemeError: Reading from “node:util” is not handled by plugins (Unhandled scheme)

Webpack supports "data:" and "file:" URIs by default.
You may need an additional plugin to handle "node:" URIs.
    at C:UsersAbhimanyuDesktoppdf-signpdf-esign-demonode_moduleswebpacklibNormalModule.js:984:10
    at Hook.eval [as callAsync] (eval at create (C:UsersAbhimanyuDesktoppdf-signpdf-esign-demonode_modulestapablelibHookCodeFactory.js:33:10), <anonymous>:6:1)
    at Hook.CALL_ASYNC_DELEGATE [as _callAsync] (C:UsersAbhimanyuDesktoppdf-signpdf-esign-demonode_modulestapablelibHook.js:18:14)
    at Object.processResource (C:UsersAbhimanyuDesktoppdf-signpdf-esign-demonode_moduleswebpacklibNormalModule.js:980:8)
    at processResource (C:UsersAbhimanyuDesktoppdf-signpdf-esign-demonode_modulesloader-runnerlibLoaderRunner.js:220:11)
    at iteratePitchingLoaders (C:UsersAbhimanyuDesktoppdf-signpdf-esign-demonode_modulesloader-runnerlibLoaderRunner.js:171:10)
    at runLoaders (C:UsersAbhimanyuDesktoppdf-signpdf-esign-demonode_modulesloader-runnerlibLoaderRunner.js:398:2)
    at NormalModule._doBuild (C:UsersAbhimanyuDesktoppdf-signpdf-esign-demonode_moduleswebpacklibNormalModule.js:965:3)
    at NormalModule.build (C:UsersAbhimanyuDesktoppdf-signpdf-esign-demonode_moduleswebpacklibNormalModule.js:1155:15)
    at C:UsersAbhimanyuDesktoppdf-signpdf-esign-demonode_moduleswebpacklibCompilation.js:1422:12

Passing Data between components on route change using Next.js 15

I’ve got a small problem:

Context:

I have a a react page that contains, one component with two sub-components, the first sub-component contains the filters, and the second sub-component contains links for the same page(just a different title), so when I click on one link I get redirected to the same main component, just with different route, as shown below:

This is my design:

enter image description here

Since all the links point to the same main component, on the server side(using Next.js), I used generateStaticParams to dynamically generate all those pages.

Problem:

When I click on one of the links(Link component), I want to keep the same filter values(Filters component). So I want to keep the same state as the previous page by going to the new page with a different route.
The problem is that every time I click on one of the links, the new page is generated from the server side and I lose all the client data.

I’m looking for a good approach to solve this by not using localStorage. And I don’t want to pass the data as query parameters using <Link> from Next.
I thought also of using the Context, but it’s available just on the client side and the data will be lost after changing the route.

Is there a way to solve this, even by changing the current design?

I’m using React 19 and Next.js 15, any help will be very appreciated.

Video.js returns CORS error policy when fetching Cloudfront content

Summary

Currently I’m trying to build a website that displays private videos in MPEG DASH format with Video.JS based on user privileges. I’m hosting the frontend with NextJS (https://development.domain.com) and the backend with NestJS (https://api.development.domain.com). I’m storing the video in a private S3 bucket, and use CloudFront to generate signed cookies (if user is authorized) and display the content.

Frontend address: https://development.domain.com
Backend address: https://api.development.domain.com
CDN (Cloudfront) address: https://cdn-development.domain.com

The Problem

If I access the video directly https://cdn-development.domain.com/private/intro.mpd, and the cookies: CloudFront-Key-Pair-Id, CloudFront-Policy, CloudFront-Signature are stored correctly in the browser page I can view the video just fine. However if I try to play this in my video player in the NextJS app in the video does not play and it returns:

The value of the “Access-Control-Allow-Origin” header in the response must not be the wildcard “*” when the request’s credentials mode is “include”. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

S3 Bucket – CORS Policy

enter image description here

Cloudfront

enter image description here
enter image description here

Single input field of rollupOptions in vite causing 404 block

I am using vite as dev server for office-addin develop, not very familar with vite config.

There is a very confused problem when I editting the build.rollupOptions.input

here is my config below:

        ...
        root: "src/taskpane",
        build: {
            rollupOptions: {
                input: {
                    taskpane: "taskpane.html"
                },
            },
            outDir: "../dist",
            emptyOutDir: true
        },
        ...

If there is only one field belonged to input, it will get 404 status code when I try to visit the web https://localhost::port/taskpane.html. But if there are more than one filed in input, then I could visit https://localhost::port/taskpane.html even the other entrypoint is not available like this

        ...
        root: "src/taskpane",
        build: {
            rollupOptions: {
                input: {
                    taskpane: "taskpane.html",
                    anything: "anything.html"
                },
            },
            outDir: "../dist",
            emptyOutDir: true
        },
        ...

I also tried input: "taskpane.html" and input: ["taskpane.html"], not work yet. My workspace struce is like this:

src
  taskpane
    taskpane.html

while src and taskpane are directory.

I only have one entrypoint so I want to keep only one field in input config, any idea will be welcome.

textarea value ignoring leading new lines

I have a textarea that looses the first n character every time it is set:

Set value on page load …

php code: nn1234 textarea value: n1234

Resend new value …

php code: n1234 textarea value: 1234

So one-by-one the n‘s are getting cut.

Of course, I can prevent this by adding a n before sending the value to the textarea (if necessary) and the same on form submit I can do the same with the textarea value before sending it or after receiving it in my php code.

But it feels a bit hacky. Is there a proper way to do this? By the way I tried rn, too. But the result is basically the same.

I don’t understand how to postpone a task

I need to do the processing of all new tasks every 5 seconds.

My goal:

  • We take multiple tasks to process
  • Set the status of the task to active (can’t find a way)
    • active is the status of the task itself in bullmq.
  • We use the data from these tasks to send a single request to a third-party SMS sending service
  • This service will return us the data, which will contain the status and some data (which we don’t really need).
  • If some time passes (for example 30 seconds) and nothing happens to the task – it should repeat. (This is most likely the responsibility of stalledInterval)
  • A third-party service will update the status of the task on the route let’s say /sms/status.

The problem I encountered is that the tasks do not go to the “active” status, because of which they are repeated (selected by timer) every 5 seconds.

import { InjectQueue } from "@nestjs/bullmq";
import { Inject, Injectable, Logger, type OnModuleDestroy, type OnModuleInit } from "@nestjs/common";
import { seconds } from "@nestjs/throttler";
import { Job, Worker, type Queue } from "bullmq";
import Redis from "ioredis";

@Injectable()
export class SmsQueueProcessor implements OnModuleInit, OnModuleDestroy
{
    private readonly logger = new Logger(SmsQueueProcessor.name);
    private intervalTimer: NodeJS.Timeout;
    private worker: Worker;

    constructor(
        @Inject("REDIS_CONNECTION")
        private readonly redisConnection: Redis,

        @InjectQueue("smsQueue")
        private readonly smsQueue: Queue<ISendPayload>
    ) { }

    async onModuleInit()
    {
        this.worker = new Worker(
            "smsQueue",
            null, // Remove automatic processing of a new task
            {
                connection: this.redisConnection,
                stalledInterval: seconds(30)
            }
        );

        this.worker.on("failed", (job, err) =>
        {
            this.logger.error(`Job ${ job.id } failed:`, err.message);
        });

        this.intervalTimer = setInterval(async () =>
        {
            const jobs = await this.smsQueue.getJobs([ "waiting" ]);

            if (jobs.length > 0)
            {
                this.processBulk(jobs);
            }
        }, 5000);
    }

    async onModuleDestroy()
    {
        if (this.intervalTimer)
        {
            clearInterval(this.intervalTimer);
        }

        await this.worker.close();
    }

    async processBulk(jobs: Job<ISendPayload>[])
    {
        this.logger.log(`Processing processBulk (items: ${ jobs.length } )`);

        try
        {
            const payloads = jobs.map(({ data }) => data); // Getting data from tasks
            const { data } = await this.sendBulk(payloads); // Send a request to a third-party service with all data

            for (const job of jobs)
            {
                const res = data.find((item) => item.extra_id === job.id);

                if (res)
                {
                    const token = crypto.randomUUID();

                    await job.extendLock(token, seconds(30));
                    await this.handleJobResult(job, res);
                }
                else
                {
                    this.logger.warn("No result found for job " + job.id);
                }
            }
        }
        catch (error)
        {
            this.logger.error("Error processing jobs:", error.message);
        }
    }

    private async sendBulk(data)
    {
        // Some kind of query that will return data to us:

        return {
            data: data.map((data) =>
            {
                return {
                    extra_id: data.extra_id,
                    status: "send"
                } satisfies ISendResponse;
            })
        };
    }

    private async handleJobResult(job: Job<ISendPayload>, res: ISendResponse)
    {
        if (res.err)
        {
            this.logger.error("Error sending SMS: " + res.err);
            await job.moveToFailed(new Error(res.err), job.token);
        }
        else
        {
            switch (res.status)
            {
                case "send":
                    await job.log("In progress: " + res.status);
                    await job.updateProgress(50);
                    break;

                case Status.Delivered:
                    await job.log("Delivered");
                    await job.updateProgress(100);
                    await job.moveToCompleted("Delivered", job.token);
                    break;

                case Status.Undelivered:
                    await job.moveToFailed(new Error("SMS Undelivered"), job.token);
                    break;

                default:
                    await job.log(`Unknown status: ${ res.status }`);
                    this.logger.error(`Unknown status for job ${ job.id }: ${ res.status }`);
            }
        }
    }
}