Docker Compose. Port is assigned but when I netstat it shows nothing

I’ve no idea why this is happening and I am not good at asking questions.

It was fine a week ago after someone pushed this problem occured.

We are using Gitlab. just in-case.

Here is Docker Compose

services:
  backend-app:
    image: backend-app:latest
    build:
      context: .
    container_name: mdm_backend_app
    ports:
      - "8080:8080"
    networks:
      - app-network
    environment:
      - staticHesXmlPath=/mnt/clouhes
    volumes:
      #- ~/files/logs:/mnt/clouhes
      - ~/files/:/files/
      - /home/user/clouhes:/mnt/clouhes
    logging:
      driver: "json-file"
      options:
        max-size: "20m"
        max-file: "5"
    restart: unless-stopped

networks:
  app-network:
    driver: bridge

And Dockerfile

FROM maven:3.8.4-openjdk-17 AS build

WORKDIR /app

COPY . .

RUN mvn clean package -DskipTests

FROM openjdk:17-jdk-alpine
RUN apk add --no-cache tzdata
ENV TZ=World/Nowhere
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
WORKDIR /app

COPY --from=build /app/target/backend-0.0.1-SNAPSHOT.jar app.jar
RUN mkdir -p /logs
RUN mkdir -p /files
EXPOSE 8080

ENTRYPOINT ["java", "-jar", "/app/app.jar", "--spring.profiles.active=test"]

And when I try docker ps -a it shows 8080 port is mapped

CONTAINER ID   IMAGE                           COMMAND                  CREATED       STATUS       PORTS                                       NAMES
2338761f2a68   backend-app-image:latest    "java -jar /app/app.…"   2 hours ago   Up 2 hours   0.0.0.0:8080->8080/tcp, :::8080->8080/tcp   backend_app

Then when I try to access 8080 it’s not working and after that I tried Netstat inside the Container and I am getting Nothing

user@ubuntu:~$ docker exec -it backend_app netstat -tulpn | grep 8080
user@ubuntu:~$

Is there something Wrong? I’ve no idea quite literally. I almost tried everything i knew. probably Enviroment Error? or something? First Time occuring.

The App is working just as intended on the background. but I can’t access it since the port is not assigned?

And I’ve seen the Log. Just in case here it is.

docker logs 2338761f2a68

  .   ____          _            __ _ _
 /\ / ___'_ __ _ _(_)_ __  __ _    
( ( )___ | '_ | '_| | '_ / _` |    
 \/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |___, | / / / /
 =========|_|==============|___/=/_/_/_/

 :: Spring Boot ::                (v3.3.3)

2025-02-24 15:54:59 - Starting BackendApplication v0.0.1-SNAPSHOT using Java 17-ea with PID 1 (/app/app.jar started by root in /app)
2025-02-24 15:54:59 - The following 1 profile is active: "test"
2025-02-24 15:55:00 - Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2025-02-24 15:55:01 - Finished Spring Data repository scanning in 194 ms. Found 24 JPA repository interfaces.
2025-02-24 15:55:01 - Bean 'webServiceConfigSoap' of type [com.backend.backend.addons.smart_meter.config.WebServiceConfigSoap$$SpringCGLIB$$0] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected into a currently created BeanPostProcessor [annotationActionEndpointMapping]? Check the corresponding BeanPostProcessor declaration and its dependencies.
2025-02-24 15:55:01 - Bean 'org.springframework.ws.config.annotation.DelegatingWsConfiguration' of type [org.springframework.ws.config.annotation.DelegatingWsConfiguration$$SpringCGLIB$$0] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). The currently created BeanPostProcessor [annotationActionEndpointMapping] is declared through a non-static factory method on that class; consider declaring it as static instead.
2025-02-24 15:55:01 - Supporting [WS-Addressing August 2004, WS-Addressing 1.0]
2025-02-24 15:55:02 - Tomcat initialized with port 8080 (http)
2025-02-24 15:55:02 - Initializing ProtocolHandler ["http-nio-0.0.0.0-8080"]
2025-02-24 15:55:02 - Starting service [Tomcat]
2025-02-24 15:55:02 - Starting Servlet engine: [Apache Tomcat/10.1.28]
2025-02-24 15:55:02 - Initializing Spring embedded WebApplicationContext
2025-02-24 15:55:02 - Root WebApplicationContext: initialization completed in 3587 ms
2025-02-24 15:55:03 - HHH000204: Processing PersistenceUnitInfo [name: default]
2025-02-24 15:55:03 - HHH000412: Hibernate ORM core version 6.5.2.Final
2025-02-24 15:55:03 - HHH000026: Second-level cache disabled
2025-02-24 15:55:04 - No LoadTimeWeaver setup: ignoring JPA class transformer
2025-02-24 15:55:04 - HikariPool-1 - Starting...
2025-02-24 15:55:04 - HikariPool-1 - Added connection ConnectionID:1 ClientConnectionId: 724e6471-73dc-407c-ae8d-a58dcdac8923
2025-02-24 15:55:04 - HikariPool-1 - Start completed.
2025-02-24 15:55:07 - HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
2025-02-24 15:55:07 - Initialized JPA EntityManagerFactory for persistence unit 'default'
2025-02-24 15:55:08 - Hibernate is in classpath; If applicable, HQL parser will be used.
2025-02-24 15:55:08 - Successfully executed sql script.
2025-02-24 15:55:10 - Resolved watchPath: /mnt/clouhes
2025-02-24 15:55:10 - folderToWatch path: /mnt/clouhes
2025-02-24 15:55:10 - archiveFolder path: /mnt/clouhes/archived
2025-02-24 15:55:10 - Directory already exists: /mnt/clouhes
2025-02-24 15:55:10 - Directory already exists: /mnt/clouhes/archived
2025-02-24 15:55:10 - Processing existing XML files in: /mnt/clouhes

Well… Anyone got solution?

And there is a around way to fix this.

I can just use Docker without Compose. and run it with

sudo -S docker run -d -p 8080:8080 --name backend_app backend-backend-app

In this case it works as intended Port is mapped and all.

user@ubuntu:~/.ECM/mdm-backend$ docker exec -it mdm_backend_app netstat -tulpn | grep 8080
tcp        0      0 :::8080                 :::*                    LISTEN      1/java

But it’s not what I want.

I need

    environment:
      - staticHesXmlPath=/mnt/clouhes
    volumes:
      #- ~/files/logs:/mnt/clouhes
      - ~/files/:/files/
      - /home/user/clouhes:/mnt/clouhes

This Syncs 2 Folders and Load XML Files.

Anyone got good solution here? or I just gotta use Docker without compose?

Well. Why is the Port not assigned inside the Container when I use Docker Compose.
I need to find out the issue here and fix it.

In short I need to Assign 8080 port to Container when I am using Docker Compose.

bullmq deduplicate job while keeping the last one in the queue

I have a bullMq queue (deplo_site) that takes around 20 seconds to finish. There should be no concurrent run per site, so basic deduplication was easy:

deployQueue.add({
    id: siteId
  },
  {
    deduplication: {
      id: siteName
    }
  });

But the problem is, if user changes the config and wants to update it during a job running, I lose that job because of deduplication. Then I can never deploy the site with the latest configuration unless trying again.

What is the best way of handling that kind of issues?

IFrame / Javascript not working in mobile browsers [duplicate]

I have a html page which embeds a pdf in an iframe. The page is deployed as a static site in AWS S3 . (http endpoint)

When the user lands on the page , a modal dialog containing an image is shown. I also need to find the user location of the user. I am extracting user location from the user ip-address.

I store the fetched to AWS DynamoDB by calling AWS lambda function url.

The whole thing is working nicely on desktop browsers. The moment , i try to access from mobile , android or iOS , the page does not load. Just a blank screen.

Couple things that i tried …

  • iframe source to https endpoint
  • use embed instead of iframe.

Also , I am using both window.addEventListener(‘DOMContentLoaded’ and document.addEventListener(“DOMContentLoaded”). if this is an issue or i am using it incorrectly.

Really struggling to get this working. Any help / pointer would be helpful Thanks

const myModal = new bootstrap.Modal('#load-modal');

window.addEventListener('DOMContentLoaded', function() {
  myModal.show();
});


document.addEventListener("DOMContentLoaded", function() {
  console.log("Inside addeventlistener")
  // Fetch latitude and longitude based on IP address
  fetch("https://ipapi.co/json")
    .then(response => response.json())
    .then(data => {
      console.log(data.latitude)
      console.log(data.longitude)
      const bdcAPI = `https://api-bdc.net/data/reverse-geocode-client?
                // latitude=${data.latitude}&
                // longitude=${data.longitude}`
      getAPI(bdcAPI)
    })
    .catch(error => {
      console.error("Error fetching IP address:", error);
    });
});

function getAPI(bdcAPI) {
  fetch(bdcAPI)
    .then(response => response.json())
    .then(data => {
      console.log(data.countryName)
      console.log(data.city)

      functionURL(data.continentCode, data.continent, data.countryCode,
        data.countryName, data.principalSubdivisionCode, data.principalSubdivision, data.city, data.locality)
    })
    .catch(error => {
      console.error("Error fetching country and city name", error);
    });
}

function functionURL(continentCode, continent, countryCode, countryName,
  principalSubdivisionCode, principalSubdivision, city, locality) {

  const functionurl = `https://placeholder.lambda-url.ap-south-1.on.aws/?continentCode=${continentCode}&continent=${continent}&countryCode=${countryCode}&countryName=${countryName}&principalSubdivisionCode=${principalSubdivisionCode}&principalSubdivision=${principalSubdivision}&city=${city}&locality=${locality}`

  console.log(functionurl)

  fetch(functionurl)
    .then(response => response.json())
    .then(data => {
      console.log('Location logged successfully!!')
    })
    .catch(error => {
      console.error("Error calling function url:", error);
    });
}
<html>

<head>
  <title> Macleods Journal </title>
  <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">

  <script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
  <script defer src="script.js"></script>

  <link rel="icon" href="data:, ">
</head>

<body>
  <p></p>

  <p class="container text-center fs-4 fw-bold" style="color: #FFFFFF; background-color: #9F232B;">Journal of The Association of Physicians of India (2025)</p>

  <div class="modal fade" id="load-modal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
    <div class="modal-dialog modal-dialog-centered " role="document" style="width: 70%;height: 70%; max-width: 80%;max-height: 80%;">
      <div class="modal-content">
        <div class="d-flex justify-content-end">
          <button type="button" class="close rounded-2 " data-bs-dismiss="modal" aria-label="Close" style="margin-right: 5px;margin-top: 5px;">
                    X
                  </button>
        </div>
        <div class=" text-center ">
          <img src="advertisment.jpg" class="rounded-20" style="width: 95%;height: 100%; margin-left: 10px;margin-bottom: 10px;margin-right: 10px;"/>
        </div>

      </div>
    </div>
  </div>
  <div class="text-center" style="width: 100%;height: 100%;">
    <iframe class="pdf d-flex justify-content-center"
          style="border-radius:20px;padding:5px;"
          src= "JAPI0844_1st_proof.pdf#zoom=170"
          width="100%"
          height="100%">
      </iframe>
  </div>
</body>

</html>

Workaround for setting cookies during rendering in NextJS app router

I am making an integration for a CMS that may return variants of content elements. I want different users to see different variants, but the same users to always get the same variants between refreshes, so I setup the backend to store their selected variants against a session Id that I store in cookies.

This works fine with the pages router because I can read and write cookies in getServerSideProps, something like this:

export const getServerSideProps = async (context) => {
  let sessionId = context.req.cookies['sessionId']
  if (!sessionId) {
    sessionId = createSessionId();
    setCookie('sessionId', sessionId);
  }

  const content = await getContent({
    sessionId
  });

  return {
    props: content,
  }
}

This means the same user will always see the same content because they will always have the same session Id.

But for the life of me I cannot figure out how to make this work with the app router. I don’t want the SDK consumers to have to implement a really convoluted solution on their own.

I was thinking it might be better to use the user’s IP address rather than a cookie. Is this feasible? Some hosting providers don’t provide the IP address.

Javascript rounding issue difference [duplicate]

This is going to be my first question. Just started Automation with javascript and stuck on rounding issue. In this screen shot not sure value of A is different than B even the formula is same?

My Ideal expectation of value a is 0.1985 and b is 0.3035

let a = 0.198450;let b = 0.303450;
a = Math.round(a * 10000) / 10000;
b = Math.round(b * 10000) / 10000;
console.log("a Value is :  "+a);
console.log("b Value is : "+b);

how to change the grid size in the same total space? is it the loop approach that’s making it not work?

const html = document.querySelector("html");
const body = document.querySelector("body");
const mainContainer = document.querySelector(".main-container");

html.style.margin = "0px";
html.style.padding = "0px";

body.style.margin = "0px";
body.style.padding = "0px";

mainContainer.style.width = "400px";
mainContainer.style.height = "400px";

for (s = 0; s < 16; s++) {
  const firstColumn = document.createElement("div");
  firstColumn.style.display = "flex";

  mainContainer.appendChild(firstColumn);
  for (i = 0; i < 16; i++) {
    const tinySquare = document.createElement("div");
    tinySquare.className = "tiny-square";
    tinySquare.style.height = "20px";
    tinySquare.style.width = "20px";
    tinySquare.style.border = "solid 2px black";
    firstColumn.appendChild(tinySquare);
    tinySquare.addEventListener("mouseover", () => (tinySquare.style.backgroundColor = "red"));
  }

}
<div class="main-container"></div>

i tried turning firstColumn into a flex but still didnt work and no matter how much i fix the mainContainer’s width and height it still gets out of it

i want to make a button to choose a fixed amount of squares in the grid (16×16, 32×32… etc).So im expecting the user to change the (i < 16) and (s < 16) values

changing the (i < 16) value works well by increasing the squares but still takes the same amount of pixels. firstColumn on the other hand keeps increasing vertically outside the container

Multiple image trails following cursor with Javascript

I’m trying to create multiple image trails for a grid. Each trail follow the cursor, I found an example on Codepen to illustrate the effect I’m looking for. The example is made with GSAP, but for this project I would prefer not using any libraries.

I’ve success to make the block of images following the cursor but I don’t find the way to reproduce the effect in Javascript.

const posts = document.querySelectorAll('.js-post');

let activePost = null;
let activeCursor = null;
let currentX = 0, 
    currentY = 0;
let aimX = 0, 
    aimY = 0;
const speed = 0.2;

const animate = () => {
  if (activeCursor) {
    currentX += (aimX - currentX) * speed;
    currentY += (aimY - currentY) * speed;
    activeCursor.style.left = currentX + 'px';
    activeCursor.style.top = currentY + 'px';
  }
  requestAnimationFrame(animate);
};

animate();

posts.forEach(post => {
  
  

post.addEventListener('mouseenter', (e) => {
    // Hide the previous grid element's cursor immediately, if any.
    if (activePost && activePost !== post && activeCursor) {
      activeCursor.classList.remove('is-visible');
      // Reset the previous cursor to 0,0 relative to its container.
      activeCursor.style.left = '0px';
      activeCursor.style.top = '0px';
    }
    activePost = post;
    activeCursor = post.querySelector('.js-cursor');

    // Get grid item's bounding rectangle for local coordinate conversion.
    const rect = post.getBoundingClientRect();
    currentX = e.clientX - rect.left;
    currentY = e.clientY - rect.top;
    aimX = currentX;
    aimY = currentY;
    
    // Position the cursor immediately at the mouse's location.
    activeCursor.style.left = currentX + 'px';
    activeCursor.style.top = currentY + 'px';
    activeCursor.classList.add('is-visible');
  });

  post.addEventListener('mousemove', (e) => {
    if (activePost === post && activeCursor) {
      const rect = post.getBoundingClientRect();
      aimX = e.clientX - rect.left;
      aimY = e.clientY - rect.top;
    }
  });

  post.addEventListener('mouseleave', () => {
    if (activePost === post && activeCursor) {
      activeCursor.classList.remove('is-visible');
      // Reset the coordinates to the top-left (0,0) of the grid element.
      activeCursor.style.left = '0px';
      activeCursor.style.top = '0px';
      // Also reset the internal coordinates so the next activation starts from 0,0.
      currentX = 0;
      currentY = 0;
      aimX = 0;
      aimY = 0;
      activePost = null;
      activeCursor = null;
    }
  });
});
body{
  font-family: 'helvetica', arial, sans-serif;
}

.grid{
  display: grid;
  width: 100%;
  grid-template-columns: repeat(2, 1fr);
  grid-column-gap: 1rem;
  grid-row-gap: 1rem;
}

.grid__item{
  display: flex;
  justify-content: center;
  align-content: center;
  position: relative;
  padding: 25%;
  overflow: hidden;
  background-color: #333;
}

.grid__item-number{
   color: #888;
   font-size: 5rem;
}

.grid__item-cursor{
  position: absolute;
  width: 150px;
  height: 200px;
  transform: translate(-50%, -50%);
  pointer-events: none;
  z-index: -1;
  opacity: 0;
  
  transition: opacity .3s ease .1s;
}

.grid__item-cursor.is-visible{
  z-index: 1;
  opacity: 1;
}

.grid__item-image{
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
<div class="grid">
  
  <div class="grid__item js-post">
    <div class="grid__item-number">1</div>
    <div class="grid__item-cursor js-cursor">
       <img class="grid__item-image js-image" src="https://cdn.pixabay.com/photo/2022/11/13/18/09/canyon-7589820_1280.jpg">
      <img class="grid__item-image js-image" src="https://cdn.pixabay.com/photo/2022/11/02/22/33/autumn-7566201_1280.jpg">
      <img class="grid__item-image js-image" src="https://cdn.pixabay.com/photo/2023/04/05/09/44/landscape-7901065_1280.jpg">
      <img class="grid__item-image js-image" src="https://cdn.pixabay.com/photo/2020/09/04/16/18/mountains-5544365_1280.jpg">
    </div>
  </div>
  
  <div class="grid__item js-post">
    <div class="grid__item-number">2</div>
    <div class="grid__item-cursor js-cursor">
      <img class="grid__item-image js-image" src="https://cdn.pixabay.com/photo/2022/11/13/18/09/canyon-7589820_1280.jpg">
      <img class="grid__item-image js-image" src="https://cdn.pixabay.com/photo/2022/11/02/22/33/autumn-7566201_1280.jpg">
      <img class="grid__item-image js-image" src="https://cdn.pixabay.com/photo/2023/04/05/09/44/landscape-7901065_1280.jpg">
      <img class="grid__item-image js-image" src="https://cdn.pixabay.com/photo/2020/09/04/16/18/mountains-5544365_1280.jpg">
    </div>
  </div>
  
  <div class="grid__item js-post">
    <div class="grid__item-number">3</div>
    <div class="grid__item-cursor js-cursor">
      <img class="grid__item-image js-image" src="https://cdn.pixabay.com/photo/2022/11/13/18/09/canyon-7589820_1280.jpg">
      <img class="grid__item-image js-image" src="https://cdn.pixabay.com/photo/2022/11/02/22/33/autumn-7566201_1280.jpg">
      <img class="grid__item-image js-image" src="https://cdn.pixabay.com/photo/2023/04/05/09/44/landscape-7901065_1280.jpg">
      <img class="grid__item-image js-image" src="https://cdn.pixabay.com/photo/2020/09/04/16/18/mountains-5544365_1280.jpg">
    </div>
  </div>
  
  <div class="grid__item js-post">
    <div class="grid__item-number">4</div>
    <div class="grid__item-cursor js-cursor">
      <img class="grid__item-image js-image" src="https://cdn.pixabay.com/photo/2022/11/13/18/09/canyon-7589820_1280.jpg">
      <img class="grid__item-image js-image" src="https://cdn.pixabay.com/photo/2022/11/02/22/33/autumn-7566201_1280.jpg">
      <img class="grid__item-image js-image" src="https://cdn.pixabay.com/photo/2023/04/05/09/44/landscape-7901065_1280.jpg">
      <img class="grid__item-image js-image" src="https://cdn.pixabay.com/photo/2020/09/04/16/18/mountains-5544365_1280.jpg">
    </div>
  </div>
  
</div>

ThreeJS 0.173.0 giving error Failed to resolve module specifier “three”. Relative references must start with either “/”, “./”, or “../”

This is my simplest threeJS app page. But i am getting this error :

goo-webxr-ac.web.app/:1 Uncaught TypeError: Failed to resolve module
specifier “three”. Relative references must start with either “/”,
“./”, or “../”.

You can also check at this website via opening the console. goo-webxr-ac.web.app

MyCode:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>WebXR AR</title>

</head>
<body>

<button onclick="activateXR()">Start Web AR</button>

<script type="module">
  import * as THREE from 'https://unpkg.com/[email protected]/build/three.module.js';
  import { GLTFLoader } from 'https://unpkg.com/[email protected]/examples/jsm/loaders/GLTFLoader.js';

  console.log("Three.js and GLTFLoader imported successfully!");

  async function activateXR() {
      console.log("Activating XR...");

      const canvas = document.createElement("canvas");
      document.body.appendChild(canvas);
      const gl = canvas.getContext("webgl", {xrCompatible: true});

      if (!gl) {
          console.error("WebGL is not supported!");
          return;
      } else {
          console.log("WebGL initialized successfully!");
      }

      const scene = new THREE.Scene();
      const renderer = new THREE.WebGLRenderer({ alpha: true, preserveDrawingBuffer: true, canvas: canvas, context: gl });
      renderer.autoClear = false;

      console.log("Renderer created!");

      const camera = new THREE.PerspectiveCamera();
      camera.matrixAutoUpdate = false;

      try {
          const session = await navigator.xr.requestSession("immersive-ar", { requiredFeatures: ['hit-test'] });
          console.log("XR Session started!");
          
          session.updateRenderState({ baseLayer: new XRWebGLLayer(session, gl) });

          const referenceSpace = await session.requestReferenceSpace('local');
          const viewerSpace = await session.requestReferenceSpace('viewer');
          const hitTestSource = await session.requestHitTestSource({ space: viewerSpace });

          console.log("Reference Spaces & Hit Test Source set up!");

          const loader = new GLTFLoader();
          let reticle;
          loader.load("https://immersive-web.github.io/webxr-samples/media/gltf/reticle/reticle.gltf", function (gltf) {
              reticle = gltf.scene;
              reticle.visible = false;
              scene.add(reticle);
              console.log("Reticle loaded successfully!");
          });

          session.addEventListener("select", () => {
              if (reticle) {
                  console.log("Select event triggered!");
                  reticle.visible = true;
              }
          });

          session.requestAnimationFrame((time, frame) => {
              console.log("Rendering frame...");
              renderer.render(scene, camera);
          });

      } catch (error) {
          console.error("Error initializing XR session:", error);
      }
  }
</script>


</body>
</html>

highlight all the child nodes on click of node for D3-DAG Sugiyama

I am using D3-DAG Sugiyama to render DAG. On click of a node, I need to highlight all its child nodes. I am able to get only the data (id’s of node and its child) and not the node on click. const nodeB = dag.descendants().find(node => node.id() === p.data.id) is not returning the node. This is what I tried.

  const dag = d3.dagStratify()(data);
        const nodeRadius = 15;


        const layout = d3
            .sugiyama() // base layout
            .layering(d3.layeringLongestPath())
             .nodeSize((node) => {
               return [(node ? 3.6 : 0.25) * nodeRadius, 3 * nodeRadius];
                })
        const { width, height } = layout(dag);


        // --------------------------------
        // This code only handles rendering
        // --------------------------------
        const svgSelection = d3.select("svg");
        svgSelection.attr("viewBox", [0, 0, width, height].join(" "));
        const defs = svgSelection.append("defs"); // For gradients
        const trans = svgSelection.transition().duration(750);

        const steps = dag.size();
        const interp = d3.interpolateRainbow;
        const colorMap = new Map();
        for (const [i, node] of dag.idescendants().entries()) {
            colorMap.set(node.data.id, interp(i / steps));
        }

        //const nodeB = dag.descendants().find(node => node.id() === "B");


        // How to draw edges
       const line = d3
            .line()
            .curve(d3.curveCatmullRom)
            .x((d) => d.x)
            .y((d) => d.y);

       //  Plot edges
        svgSelection
            .append("g")
            .selectAll("path")
            .data(dag.links())
            .enter()
            .append("path")
            .attr("d", ({ points }) => line(points))
            .attr("fill", "none")
            .attr("stroke-width", 1)
            .attr("stroke", ({ source, target }) => {
                // encodeURIComponents for spaces, hope id doesn't have a `--` in it
                const gradId = encodeURIComponent(`${source.data.id}--${target.data.id}`);
                const grad = defs
                    .append("linearGradient")
                    .attr("id", gradId)
                    .attr("gradientUnits", "userSpaceOnUse")
                    .attr("x1", source.x)
                    .attr("x2", target.x)
                    .attr("y1", source.y)
                    .attr("y2", target.y);
                grad
                    .append("stop")
                    .attr("offset", "0%")
                    .attr("stop-color", colorMap.get(source.data.id));
                grad
                    .append("stop")
                    .attr("offset", "100%")
                    .attr("stop-color", colorMap.get(target.data.id));
                   return `url(#${gradId})`;
            });

        // Select nodes
        const nodes = svgSelection
            .append("g")
            .selectAll("g")
            .data(dag.descendants())
            .enter()
            .append("g")
            .attr("transform", ({ x, y }) => `translate(${x}, ${y})`);


        var titleTip = d3.select("body")
            .append("div")
            .style("position", "absolute")
            .style("z-index", "10")
            .style("visibility", "hidden")
            .style("border", "1px thin black")
            .style("background-color", "#D3D3D3")
            .style("stroke", "#000")
            .style("stroke-width", "10");
 


          //   Plot node circles
            nodes
                .append("circle")
                .attr("id", "circleBasicTooltip")
                .attr("r", nodeRadius)
                .attr("fill", (n) => colorMap.get(n.data.id))


            nodes
                .each(function (p, j) {
                    d3.select(this)
                        .on("click", (selectedNode) => {
                            selectedNode.srcElement.attributes.fill = 'grey';
                            const nodeB = dag.descendants().find(node => node.id() === p.data.id);// not able to get the node
                            const highlightedNode = p.data.id;
                        })

                })

            const arrowSize = (nodeRadius * nodeRadius) / 10.0;
            const arrowLen = Math.sqrt((4 * arrowSize) / Math.sqrt(3));
            const arrow = d3.symbol().type(d3.symbolTriangle).size(arrowSize);
            svgSelection
                .append("g")
                .selectAll("path")
                .data(dag.links())
                .enter()
                .append("path")
                .attr("d", arrow)
                .attr("transform", ({ points }) => {
                    var twoPoints = points.slice(-2);
                    const sx = twoPoints[0].x;
                    const sy = twoPoints[0].y;
                    const ex = twoPoints[1].x;
                    const ey = twoPoints[1].y;
                    const dx = sx - ex;
                    const dy = sy - ey;
                    // This is the angle of the last line segment
                    const scale = nodeRadius * 1.15 / Math.sqrt(dx * dx + dy * dy);
                    const angle = (Math.atan2(-dy, -dx) * 180) / Math.PI + 90;
                    return `translate(${ ex + dx * scale }, ${ ey + dy * scale }) rotate(${ angle })`

                })
                .attr("stroke", "white")
                .attr("stroke-width", 1.5)
                .attr("stroke-dasharray", `${arrowLen},${arrowLen}`);




        //Add text to nodes
        nodes
            .append("text")
            .text((d) => d.data.id)
            .attr("font-weight", "bold")
            .attr("font-family", "sans-serif")
            .attr("text-anchor", "middle")
            .attr("alignment-baseline", "middle")
            .attr("fill", "white")

Is there a JavaScript carousel which is fully responsive without having to specify breakpoints or “slidesToShow”? [closed]

I’m looking for a slider/carousel which automatically responds as the browser window changes size. If the window gets wider the number of visible slides should increase, and if the window gets smaller the number of visible slides should decrease.

The popular carousel plugins such as slick.js seem to require you to specify breakpoints in their code via the breakpoint and slidesToShow properties.

I am looking for a more flexible solution where the carousel adjusts itself automatically to grow and shrink as required without having to specify breakpoints or number of slides to show (it’s fine if I need to specify a minimum and maximum width for the slides).

Can console log messages be updated later? [duplicate]

Is there a way to update a console.log() message after the fact?

I want to write a message like “Started Thing”
Then later when it is done have it updated to “Started Thing (Finished)”

Or does a console.log return an id like setInterval()?
That way it can be console.update(id, “New message”)

I’m using vscode

Example:

var id = console.log("Thing started");

// later 
console.update(id, "Thing started and finished at " + time)

Collision detection of two rects with rounded borders

I wrote an algorithm how to detect the collision detection of two rects with rounded borders (the code was written using TypeScript but I tried to name things as clear as possible to code be readable for anyone):

const canvas = document.querySelector("canvas");

canvas.width = window.innerWidth / 1.2;
canvas.height = window.innerHeight / 1.2;

const ctx = canvas.getContext("2d");

type Rect = {
  x: number;
  y: number;
  w: number;
  h: number;
};

type RoundedRect = Rect & {
  borderRadius: number;
};

const isFirstRectRighterThanSecond = (rect1: Rect, rect2: Rect): boolean => rect1.x + rect1.w < rect2.x;
const isFirstRectBelowThanSecond = (rect1: Rect, rect2: Rect): boolean => rect1.y + rect1.h < rect2.y;

const hasNotAABBCollision = (rect1: Rect, rect2: Rect): boolean =>
  isFirstRectRighterThanSecond(rect1, rect2) ||
  isFirstRectRighterThanSecond(rect2, rect1) ||
  isFirstRectBelowThanSecond(rect1, rect2) ||
  isFirstRectBelowThanSecond(rect2, rect1);

class Vector {
  constructor(
    x: number = 0,
    y: number = 0,
  ) {
    this.x = x;
    this.y = y
  }
}

const getRoundedRectTopLeftRoundedCornerCenter = (roundedRect: RoundedRect): Vector =>
  new Vector(roundedRect.x + roundedRect.borderRadius, roundedRect.y + roundedRect.borderRadius);

const getRoundedRectTopRightRoundedCornerCenter = (roundedRect: RoundedRect): Vector =>
  new Vector(roundedRect.x + roundedRect.w - roundedRect.borderRadius, roundedRect.y + roundedRect.borderRadius);

const getRoundedRectBottomRightRoundedCornerCenter = (roundedRect: RoundedRect): Vector =>
  new Vector(
    roundedRect.x + roundedRect.w - roundedRect.borderRadius,
    roundedRect.y + roundedRect.h - roundedRect.borderRadius,
  );

const getRoundedRectBottomLeftRoundedCornerCenter = (roundedRect: RoundedRect): Vector =>
  new Vector(roundedRect.x + roundedRect.borderRadius, roundedRect.y + roundedRect.h - roundedRect.borderRadius);

const getRoundedRectRoundedCornersCenters = (roundedRect: RoundedRect): Vector[] => [
  getRoundedRectTopLeftRoundedCornerCenter(roundedRect),
  getRoundedRectTopRightRoundedCornerCenter(roundedRect),
  getRoundedRectBottomRightRoundedCornerCenter(roundedRect),
  getRoundedRectBottomLeftRoundedCornerCenter(roundedRect),
];

const sqr = (x: number) => x * x;

const squareDistance = (vector1: Vector, vector2: Vector): number =>
  sqr(vector1.x - vector2.x) + sqr(vector1.y - vector2.y);

const doTwoCirclesCollide = (
  circle1Center: Vector,
  circle1Radius: number,
  circle2Center: Vector,
  circle2Radius: number,
): boolean => squareDistance(circle1Center, circle2Center) <= sqr(circle1Radius + circle2Radius);

const doCirclesCollide = (circles1Centers: Vector[], circles1Radius: number, circles2Centers: Vector[], circles2Radius: number): boolean =>
  circles1Centers.some(
    circle1Center => circles2Centers.some(
      circle2Center => doTwoCirclesCollide(circle1Center, circles1Radius, circle2Center, circles2Radius)
    )
  );


class Segment {
  constructor(
    start: Vector = new Vector(0, 0),
    end: Vector = new Vector(0, 0),
  ) {
    this.start = start;
    this.end = end;
  }
}

const getRoundedRectTopSegment = (roundedRect: RoundedRect): Segment =>
  new Segment(
    new Vector(roundedRect.x + roundedRect.borderRadius, roundedRect.y),
    new Vector(roundedRect.x + roundedRect.w - roundedRect.borderRadius, roundedRect.y),
  );

const getRoundedRectRightSegment = (roundedRect: RoundedRect): Segment =>
  new Segment(
    new Vector(roundedRect.x + roundedRect.w, roundedRect.y + roundedRect.borderRadius),
    new Vector(roundedRect.x + roundedRect.w, roundedRect.y + roundedRect.h - roundedRect.borderRadius),
  );

const getRoundedRectBottomSegment = (roundedRect: RoundedRect): Segment =>
  new Segment(
    new Vector(roundedRect.x + roundedRect.borderRadius, roundedRect.y + roundedRect.h),
    new Vector(roundedRect.x + roundedRect.w - roundedRect.borderRadius, roundedRect.y + roundedRect.h),
  );

const getRoundedRectLeftSegment = (roundedRect: RoundedRect): Segment =>
  new Segment(
    new Vector(roundedRect.x, roundedRect.y + roundedRect.borderRadius),
    new Vector(roundedRect.x, roundedRect.y + roundedRect.h - roundedRect.borderRadius),
  );

const getRoundedRectSegments = (roundedRect: RoundedRect): Segment[] => [
  getRoundedRectTopSegment(roundedRect),
  getRoundedRectRightSegment(roundedRect),
  getRoundedRectBottomSegment(roundedRect),
  getRoundedRectLeftSegment(roundedRect),
];

const crossProduct = (vector1: Vector, vector2: Vector): number => vector1.x * vector2.y - vector1.y * vector2.x;

const Orientations = {
  Collinear: 0,
  Clockwise: 1,
  Counterclockwise: 2,
}

const getOrientation = (vector1: Vector, vector2: Vector, vector3: Vector): Orientations => {
  const result = crossProduct(
    new Vector(vector3.x - vector2.x, vector3.y - vector2.y),
    new Vector(vector2.x - vector1.x, vector2.y - vector1.y),
  );

  if (result === 0) return Orientations.Collinear;

  if (result > 0) return Orientations.Clockwise;

  return Orientations.Counterclockwise;
};

const Axis = {
  X: "x",
  Y: "y",
}

const isDotOnSegmentProjection = (segment: Segment, dot: Vector, axis: Axis): boolean =>
  dot[axis] <= Math.max(segment.start[axis], segment.end[axis]) &&
  dot[axis] >= Math.min(segment.start[axis], segment.end[axis]);

const isDotOnSegmentProjections = (segment: Segment, dot: Vector): boolean =>
  isDotOnSegmentProjection(segment, dot, Axis.X) && isDotOnSegmentProjection(segment, dot, Axis.Y);

const doTwoSegmentsIntersect = (segment1: Segment, segment2: Segment): boolean => {
  const orientation1 = getOrientation(segment1.start, segment1.end, segment2.start);
  const orientation2 = getOrientation(segment1.start, segment1.end, segment2.end);
  const orientation3 = getOrientation(segment2.start, segment2.end, segment1.start);
  const orientation4 = getOrientation(segment2.start, segment2.end, segment1.end);

  if (orientation1 !== orientation2 && orientation3 !== orientation4) return true;

  return (
    (orientation1 === Orientations.Collinear && isDotOnSegmentProjections(segment1, segment2.start)) ||
    (orientation2 === Orientations.Collinear && isDotOnSegmentProjections(segment1, segment2.end)) ||
    (orientation3 === Orientations.Collinear && isDotOnSegmentProjections(segment2, segment1.start)) ||
    (orientation4 === Orientations.Collinear && isDotOnSegmentProjections(segment2, segment1.end))
  );
};

const doSegmentsIntersect = (segments1: Segment[], segments2: Segment[]): boolean =>
  segments1.some(
    segment1 => segments2.some(
      segment2 => doTwoSegmentsIntersect(segment1, segment2)
    )
  );

const distToSegmentSquared = (dot: Vector, segment: Segment) => {
  const squaredSegmentLength = squareDistance(segment.start, segment.end);

  if (squaredSegmentLength === 0) return squareDistance(dot, segment.start);

  const t =
    ((dot.x - segment.start.x) * (segment.end.x - segment.start.x) +
      (dot.y - segment.start.y) * (segment.end.y - segment.start.y)) /
    squaredSegmentLength;

  const clampedT = Math.max(0, Math.min(1, t));

  return squareDistance(
    dot,
    new Vector(
      segment.start.x + clampedT * (segment.end.x - segment.start.x),
      segment.start.y + clampedT * (segment.end.y - segment.start.y),
    ),
  );
};

const doCircleIntersectWithSegment = (circleCenter: Vector, circleRadius: number, segment: Segment): boolean =>
  distToSegmentSquared(circleCenter, segment) <= sqr(circleRadius);

const doCirclesIntersectWithSegments = (circlesCenters: Vector[], circlesRadius: number, segments: Segment[]): boolean =>
  circlesCenters.some(
    circleCenter => segments.some(
      segment => doCircleIntersectWithSegment(circleCenter, circlesRadius, segment)
    )
  );

const doSegmentsIntersectOnProjection = (segment1: Segment, segment2: Segment, axis: Axis): boolean =>
  isDotOnSegmentProjection(segment1, segment2.start, axis) ||
  isDotOnSegmentProjection(segment1, segment2.end, axis) ||
  isDotOnSegmentProjection(segment2, segment1.start, axis) ||
  isDotOnSegmentProjection(segment2, segment1.end, axis)

const doRoundedRectsCollide = (roundedRect1: RoundedRect, roundedRect2: RoundedRect): boolean => {
  if (hasNotAABBCollision(roundedRect1, roundedRect2)) {
    return false;
  }

  const roundedRect1CornersCenters = getRoundedRectRoundedCornersCenters(roundedRect1);
  const roundedRect2CornersCenters = getRoundedRectRoundedCornersCenters(roundedRect2);

  if (
    doCirclesCollide(
      roundedRect1CornersCenters,
      roundedRect1.borderRadius,
      roundedRect2CornersCenters,
      roundedRect2.borderRadius
    )
  ) return true;

  const roundedRect1Segments = getRoundedRectSegments(roundedRect1);
  const roundedRect2Segments = getRoundedRectSegments(roundedRect2);

  if (doSegmentsIntersect(roundedRect1Segments, roundedRect2Segments)) return true;

  if (doCirclesIntersectWithSegments(roundedRect1CornersCenters, roundedRect1.borderRadius, roundedRect2Segments)) return true;

  if (doCirclesIntersectWithSegments(roundedRect2CornersCenters, roundedRect2.borderRadius, roundedRect1Segments)) return true;

  /*
   Check if one of the rects is inside another one

   The below algorithm works only because we already tested a lot of other cases

   THIS ALGORITHM MUST NOT BE USED IN GENERAL CASE
   */

  /*
   The arguments passed in that way for optimization purposes

   If you don't want to depend on the order of elements of an array which is returned by `getRoundedRectSegments`
   then you should use `getRoundedRectTopSegment` and `getRoundedRectRightSegment` functions respectively
   */
  return (
    doSegmentsIntersectOnProjection(roundedRect1Segments[0], roundedRect2Segments[0], Axis.X) ||
    doSegmentsIntersectOnProjection(roundedRect1Segments[1], roundedRect2Segments[1], Axis.Y)
  );
};


class RoundedRectElement {
  constructor(
    ctx: CanvasRenderingContext2D,
    x: number,
    y: number,
    w: number,
    h: number,
    borderRadius: number,
    boundRectColor: string = 'white',
    roundedRectColor: string = 'green',
    circlesColor: string = 'orange',
    circlesCentersColor: string = 'red',
    segmentsColor: string = 'blue',
  ) {
    this.ctx = ctx;
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;
    this.borderRadius = borderRadius;
    this.boundRectColor = boundRectColor;
    this.roundedRectColor = roundedRectColor;
    this.circlesColor = circlesColor;
    this.circlesCentersColor = circlesCentersColor;
    this.segmentsColor = segmentsColor;
  }

  draw() {
    const path = new Path2D();

    path.rect(this.x, this.y, this.w, this.h);

    this.ctx.strokeStyle = this.boundRectColor;
    this.ctx.stroke(path);

    const path2 = new Path2D();

    path2.roundRect(this.x, this.y, this.w, this.h, this.borderRadius);

    this.ctx.strokeStyle = this.roundedRectColor;
    this.ctx.stroke(path2);

    const circlesCenters = getRoundedRectRoundedCornersCenters(this);

    for (const circleCenter of circlesCenters) {
      const center = new Path2D();

      center.arc(circleCenter.x, circleCenter.y, 4, 0, 2 * Math.PI);

      this.ctx.fillStyle = this.circlesCentersColor;
      this.ctx.fill(center);

      const circle = new Path2D();

      circle.arc(circleCenter.x, circleCenter.y, this.borderRadius, 0, 2 * Math.PI);

      this.ctx.strokeStyle = this.circlesColor;
      this.ctx.stroke(circle);
    }

    const segments = getRoundedRectSegments(this);

    for (const segment of segments) {
      const line = new Path2D();

      line.moveTo(segment.start.x, segment.start.y);
      line.lineTo(segment.end.x, segment.end.y);

      this.ctx.strokeStyle = this.segmentsColor;
      this.ctx.stroke(line);
    }
  }
};

const roundedRect1 = new RoundedRectElement(
  ctx,
  50,
  50,
  400,
  400,
  50
);
const roundedRect2 = new RoundedRectElement(
  ctx,
  0,
  0,
  125,
  100,
  25
);

const objects = [
  roundedRect1,
  roundedRect2
];

window.addEventListener('mousemove', (e) => {
  roundedRect2.x = e.offsetX;
  roundedRect2.y = e.offsetY;

  console.log(doRoundedRectsCollide(roundedRect1, roundedRect2));
});

const draw = () => {
  requestAnimationFrame(draw);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  objects.forEach(object => object.draw());
};

requestAnimationFrame(draw);
*,
*:before,
*:after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html {
  height: 100%;
}

body {
  display: flex;
  justify-content: center;
  align-items: center;

  background-color: black;

  height: 100%;
}

canvas {
  background-color: black;
  outline: 2px solid white;
}
<canvas></canvas>

The algorithm passes my test so for me it works perfectly BUT i think that my algorithms is so complicated and can be simplified

Here is the main steps of my algorithm:

  1. Check if here is AABB collision. If not then return false
  2. Find centers of circles whcih create the rounded borders for both rects. Then check if any of circles of first rect collides with other ones. If yes then return true
  3. Find all segments for both rects. Then check if any of segments of first rect collides with other ones. If yes then return true
  4. Check if any of segments of first rect collides with circles other one. If yes then return true
  5. Check if any of segments of second rect collides with circles other one. If yes then return true
  6. Check if one of rect is inside another one. If yes the return true
  7. Return false

Can any of this steps be skipped?

May be some math can be simplifed. Beacuse I tried to find an easy solution for all steps and as I understand they cover a lot of cases which may be I don’t need at all (for example my rects can’t bu turned, so may be I can use this to simplifying or for optimization)

I would appreciate any help!

jointjs – element:pointerdown for rightclick doesnt fire, only left click and middle button click

I’m trying to make an event for rightclicking an element in jointjs, however the event does not fire at all for right click only left click and middle click work. How to make it work for right click? Why doesn’t it work?

this.paper.on('element:pointerdown', (elementView: any, event: MouseEvent) => {
  console.log("1");
  if (event.button === 2) {
    console.log("2");
    event.preventDefault();
    this.startEdgeCreation(elementView.model, event);
  }
});

Vite Module Federation: remoteEntry.js returns HTML instead of JS

I am trying to module federation with vite-react. I did all configurations in the vite.config.js file in both host and remote. But when i am accessing the imported files of remote in the host it is showing blank screen

I am trying to set up module federation with Vite using @originjs/vite-plugin-federation. I have a remote app exposing components and a host app importing them. However, when I try to access http://localhost:5173/assets/remoteEntry.js, it returns an HTML page instead of JavaScript.

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
import federation from '@originjs/vite-plugin-federation';

export default defineConfig({
  plugins: [
    react(),
    federation({
      name: 'remote_app',
      filename: 'remoteEntry.js',
      exposes: {
        './Button': './src/button.jsx',
        './Header': './src/components/Header.jsx'
      },
      shared: ["react", "react-dom", "jotai"],
    })
  ],
  build: {
    modulePreload: false,
    target: 'esnext',
    minify: false,
    cssCodeSplit: false
  },
});

This is remote config. Trying share Button and Header

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react-swc';
import federation from '@originjs/vite-plugin-federation';

export default defineConfig({
  plugins: [
    react(),
    federation({
      name: 'host_app',
      remotes: {
        remote_app: 'http://localhost:5173/assets/remoteEntry.js'
      },
      shared: ["react", "react-dom"]
    })
  ],
  build: {
    modulePreload: false,
    target: 'esnext',
    minify: false,
    cssCodeSplit: false
  }
});

This is host config

Loading components in host App

const RemoteButton = React.lazy(() => import('remote_app/Button'))
const Header = React.lazy(() => import('remote_app/Header'))
 <Suspense fallback={<LoadingView />}>
 <Route exact path='/button' element={<RemoteButton />} />
 <Route exact path='/header' element={<Header />} />
</Suspense>

versions:
“@vitejs/plugin-react-swc”: “^3.5.0”,
“@originjs/vite-plugin-federation”: “^1.3.9”,
“react-router-dom”: “^7.2.0”,

When i navigated to /button route or /header route it is a blank screen.
For debugging i tried to import with promise function in useEffect of App.jsx

useEffect(() => {
    import("remote_app/Button")
      .then((module) => {
        console.log("RemoteButton loaded:", module);
      })
      .catch((error) => {
        console.error("Failed to load RemoteButton:", error);
      });
  }, []);

Error i the browser console:
Uncaught TypeError: Failed to fetch dynamically imported module: http://localhost:5173/assets/remoteEntry.js

in the browser console following error is shown

Network tab response for http://localhost:5173/assets/remoteEntry.js:

Status: 200
Response Body: Instead of JS, it returns the default Vite HTML page

<!doctype html>
<html lang="en">
  <head>
    <script type="module">import { injectIntoGlobalHook } from "/@react-refresh";
injectIntoGlobalHook(window);
window.$RefreshReg$ = () => {};
window.$RefreshSig$ = () => (type) => type;</script>

    <script type="module" src="/@vite/client"></script>

    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + React</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.jsx"></script>
  </body>
</html>

What I Have Tried:
Checked the remoteEntry.js URL: It loads an HTML page instead of a JS module.
Manually importing in useEffect:

useEffect(() => {
  import("remote_app/Button")
    .then((module) => {
      console.log("RemoteButton loaded:", module);
    })
    .catch((error) => {
      console.error("Failed to load RemoteButton:", error);
    });
}, []);

Expected Outcome:
http://localhost:5173/assets/remoteEntry.js should return the federated module, not an HTML page.
Remote components should load successfully in the host app.

Why is remoteEntry.js not being generated properly, and how do I fix this?
Both host and remote vite+react applications.
I tried in many ways but i can’t understand what is the mistake.

Log into Azure Enterprise Application with @azure/msal-node library using Electron

Context

I started with this Electron app provided by Microsoft – https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/samples/msal-node-samples/ElectronSystemBrowserTestApp.

You can read the README of the app for more details.

Overall, the app uses the msal-node library. It initialize a PublicClientApplication with a client id and authority associated with an Azure Enterprise Application. It then calls acquireTokenInteractive with a custom ILoopbackClient to fetch the authz code and subsequently the token. The loopback client server is running on localhost, so that requires the redirect URI on the app to be http://localhost.

Problem
I heard it is not recommended to use http://localhost as a redirect so I am trying to adapt the above approach to use a custom protocol (e.g. msal<appId>://auth) as the redirect URI.

I am running into issues. Specifically, when the flow acquires the authz code and calls my custom protocol, it fails.

Here is the browser output

enter image description here

Current Code
Here is what I have so far. I’d expect the call to my custom protocol to succeed since I have configured a handler for that scheme to resolve the authz code. Any guidance would be greatly appreciated!

// AuthProvider.js
const { PublicClientApplication } = require('@azure/msal-node');
const { shell } = require('electron');
const CustomLoopbackClient = require('./CustomLoopbackClient');

class AuthProvider {
    msalConfig;
    clientApplication;
    account;
    cache;

    constructor(msalConfig) {
        this.msalConfig = msalConfig;
        this.clientApplication = new PublicClientApplication(this.msalConfig);
        this.cache = this.clientApplication.getTokenCache();
        this.account = null;
    }
    
    async login() {
        const authResponse = await this.getTokenInteractive();
        console.log(authResponse);

        return this.handleResponse(authResponse);
    }

    async getTokenInteractive() {
        const customLoopbackClient = new CustomLoopbackClient();
        const openBrowser = async (url) => {
            await shell.openExternal(url);
        };
        const interactiveRequest = {
            scopes: [],
            openBrowser,
            loopbackClient: customLoopbackClient, // overrides default loopback client
        };

        const authResponse = await this.clientApplication.acquireTokenInteractive(interactiveRequest);
        return authResponse;
    }
}

module.exports = AuthProvider;
const { protocol } = require('electron');

class CustomLoopbackClient {
    async listenForAuthCode() {
        const authCodeListener = new Promise(
            (resolve, reject) => {
                protocol.handle("msal<appId>", (req, callback) => {
                    const requestUrl = new URL(req.url);
                    const authCode = requestUrl.searchParams.get("code");
                    if (authCode) {
                        resolve(authCode);
                    } else {
                        protocol.unhandle("msal<appId>");
                        reject(new Error("No code found in URL"));
                    }
                });
            }
        );

        return authCodeListener;
    }

    getRedirectUri() { return "msal<appId>://auth"; }

    closeServer() { protocol.unhandle("msal<appId>"); }
}

module.exports = CustomLoopbackClient;
// main.js
const path = require("path");
const { app, BrowserWindow, ipcMain } = require('electron/main')
const AuthProvider = require("./AuthProvider");

let win;
let authProvider;
const msalConfig = {
    auth: {
        clientId: "<appId>",
        authority: "https://login.microsoftonline.com/<tenantId>",
        redirectUri: "msal<appId>://auth"
    },
    cache: {
        cacheLocation: 'localStorage',
        storeAuthStateInCookie: true
    }
}

const createWindow = () => {
    win = new BrowserWindow({
        title: 'Test App',
        width: 800,
        height: 600,
        webPreferences: {
            contextIsolation: true,
            nodeIntegration: true,
        }
    });

    authProvider = new AuthProvider(msalConfig);

    win.webContents.openDevTools();
    win.loadFile(path.join(__dirname, "./app/build/index.html"));
}

app.whenReady().then(() => {
    createWindow();
});

As a related question, is there an issue using localhost as the redirect URI for a desktop app? Thanks!