Atlassian Connect Express cloud addon with REACT frontend

Hello everyone.
I have developed an Atlassian Cloud add-on using ACE and Node.js, but also I have developed a separate REACT front-end for the same add-on. I want to connect the REACT app with the ACE project.
I build the REACT app files, and put the build folder to the public folder in the project.
My route for getting the view, looks like this:

app.use(express.static(`${__dirname}/public`));
app.get('/render/config', (req, res) => {
res.sendFile(path.join(__dirname, '/public/index.html'));

From the localhost I can get the view and the content rendered from the render/config route, but when I connect to the Atlassian Cloud the config-page doesn’t render anything.
Can someone help me on this?

Few potential causes:

  • missing authenticating context on your routes
  • view engine doesn’t render contents
  • incorrect app installation

Any log entries indicating errors? authentication issues? success/fail app install?

Could you please paste the code snippet from:

  • your atlassian-connect.json file which specifies modules.configurePage configuration (name, key, url, location), it would look similar to this
    "modules": {
        "configurePage": {
            "name": {
                "value": "My App Configuration"
            },
            "key": "configure",
            "url": "/configure/",
            "location": "system.admin/globalsettings"
        },
  • route which covers module.configurePage.url value in your application, it would look similar to this
    router.get('/configure', addon.authenticate(), function (req, res) {
        res.render('configure', {
            title: 'Atlassian Connect'
        });
    });
  • render engine set up, it would look similar to this
    app.set('view engine', 'hbs')
  • layout set up, it would look similar to this
app.set('views', app.locals.config.views);
...
app.use(app.locals.config.uri, express.static(app.locals.config.public));
app.use(app.locals.config.uri, express.static(app.locals.config.modules));

Note: above examples are from the app which requires babel based pre-rendering before starting. In your case this might be different setup.

1 Like

Thank you @ViliusZigmantas for the response.
Here is the code snippet from atlassian.connect.json file

 "configurePage": {
            "key": "config-page",
            "url": "/render/config",
            "name": {
              "value": "Configuration page"
            }
          }  

Here is the route which covers the configurePage module:

app.use(express.static(`${__dirname}/public`));
app.get('/render/config', addon.authenticate(), (req, res) => {
res.sendFile(path.join(__dirname, '/public/index.html'));
});

The render engine set up is this:

app.engine('hbs', handlebarsEngine);
app.set('view engine', 'hbs');

I don’t have any authenticating or similar problems, since when I use the hbs template for rendering everything is okey like this code snippet, but when i try to sendFile() where is the build files from React app nothing seems to be rendered. So I am stuck at the the point how goes the connection between a React Front End, and ACE.

app.get('/render/config', addon.authenticate(), (req, res) => {
 res.render('configure-page.hbs');
 
});

Is there maybe also a requirement to change something in the configuration of the server-side-rendering.js file?

In one of code snippets you use res.sendFile() in another res.render(), which one is the actual code used? or you have both in your code?

In case you have both, the one that is set up (executed) earlier will respond to GET /render/config request. In case res.sendFile() is first then the response would be file contents, not the rendered output.

Another note: in res.render() there is no need to provide extension as long as you have a single file in your views folder under same name.

I write res.render() when I am using handlebars and render a single .hbs page located in the views folder. With handlebars everything works okey.
I use res.sendFile() when I try to use REACT (unsuccessfully), and I write sendFile() the index.html which is in the public folder, and that index.html file is the main HTML file of the app that includes the REACT code and provides a context for React to render to.

Could you please share the contents of index.html? Also add the source of rendered content (i.e. when you load it in Atlassian Cloud product > select “View Frame Source”)

An issue might be purely HTML/JS related. Any output/errors in the browser logs?

The index.html file is pretty large. It contains sources to the static files like css and js files. For example:

<script src="./static/js/main.72d6a2a3.chunk.js">

In the browser I can see this type of logs for every of the static file:
GET https://94df-146-255-74-40.ngrok.io/render/static/js/main.72d6a2a3.chunk.js net::ERR_ABORTED 404

Just to mention that from the localhost I get the static files and the corresponding Page, so here maybe raises the question if the ngrok tunnel can’t actually serve those files?

What referrer policy you’re setting in the code?

app.js

// Atlassian security policy requirements
// http://go.atlassian.com/security-requirements-for-cloud-apps
// HSTS must be enabled with a minimum age of at least one year
app.use(helmet.hsts({
  maxAge: 31536000,
  includeSubDomains: false
}));
app.use(helmet.referrerPolicy({
  policy: ['origin']
}));
app.use(helmet.hsts({
  maxAge: 31536000,
  includeSubDomains: false
}));
app.use(helmet.referrerPolicy({
  policy: ['origin']
}));

Did you configure ./static to be served as static files in your app?

// Mount static resource dirs
// Anything in ./public will be served up as static content
app.use('/', express.static('./public'));

Yes I wrote it like this

app.use(express.static(`${__dirname}/public`));

Where is your ./static located? Is it in the root of app folder or under render/?

i.e. these are two different paths:

  • https://<uri>/render/static/js/main.72d6a2a3.chunk.js
  • https://<uri>/static/js/main.72d6a2a3.chunk.js

Since you’re getting 404 errors in the browser - this could be your issue.

The ./static folder is under public folder
Screenshot (225)

I don’t get it where is render/ ?

See your earlier post.

The URI path is <uri>/render/static/js/main... which means that JS file was requested from there and since it doesn’t exists - you get 404 (NOT FOUND) error as expected. /render part is used since (I assume) it is set in baseUrl or configurePage.url. Since your HTML uses relative path ./static the browser would look for files in <current iframe uri path, instead of .> + /static.

For example

  • if your iframe URI is https://domain/render/config
  • and your HTML links to a script in relative path ./script/main.js
  • then browser will request main.js using following flow (very high-level)
    • config will be dropped as it is not the folder (more like a resource)
    • https://domain/render/ will have ./script/main.js added
    • ./ will be dropped
    • final full URI would be https://domain/render/script/main.js

To avoid issues like this in my application I am passing localUri parameter through app.locals to HTML layout and link scripts using full URI instead of relative paths

The reason why it works on localhost might be because on localhost you’re not using the same URI structure as on Atlassian Cloud (e.g. use URI without the /render/ context)

1 Like

Yes maybe this is the error. Can I get maybe github code, or part od app.js just to see how to use and pass the localUri parameter through app.locals
Thank you

@Ivana1

You can get local URI from addon.config.localBaseUrl(). This one simply returns the value you have set in your atlassian-connect.json (in case it was a template variable - it would be post-parsed).

ACE module already sets locals property with values for each request/response, which means that in your HBS template you just need to access it using {{localBaseUrl}} token.

layout.hbs (how I am accessing)

<!doctype html>
<html>
  <head>
    <meta name="local-base-url" content="{{localBaseUrl}}">
    ...
    <script type="text/javascript" charset="utf8" src="{{localBaseUrl}}jquery/dist/jquery.min.js"></script>
    ...
    <link rel="stylesheet" href="{{localBaseUrl}}@atlassian/aui/dist/aui/aui-prototyping.css" media="all" />
    ...

In any case, above works only if you are rendering the output through Express template engine. This won’t work when using sendFile() because the file is served as static (aka completed) content without passing any parameters/values from the application itself.

If you don’t want to use Handlebars as template engine, there are other alternatives:

1 Like

I don’t have problem to use Handlebars as template engine. Can I make a conclusion from this that we should use Handlebars as a template engine, or anything from the other alternatives, and I can’t just use sendFile()?

@Ivana1

TL;DR

The cause for your issue is incorrect path used to load static resources (JS, CSS) in your HTML. You could fix above issue by passing path from the application, but for this you would need to use template rendering.

I fixed the code with the paths, and now in the terminal in node.js I get a response that the route is served, with a response status 200OK, and also the static files:

GET /static/css/main.7d7429e5.chunk.css 200 
GET /static/js/2.afc23933.chunk.js 200

But again on the cloud instance in the configure page module there is nothing rendered?

Any errors in browser log?

When you look at the source (view frame source in the browser) do you see rendered Javascript or React source code?