Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Server.ts changes are not being process after deploying to nelify but works fine locally #135

Closed
mrrohit1 opened this issue Jun 5, 2024 · 9 comments
Assignees
Labels

Comments

@mrrohit1
Copy link

mrrohit1 commented Jun 5, 2024

Describe the bug
Update my server.ts file to manage 301 redirect and added sanitization for json in final souuce code but both changes are not working, i can see its working locally but after deploying to netlify its not working

To Reproduce
Steps to reproduce the behavior:

  1. Make 301 redirects in server.ts file
function app(): express.Express {
  const server = express();
  const serverDistFolder = dirname(fileURLToPath(import.meta.url));
  const browserDistFolder = resolve(serverDistFolder, '../browser');
  const indexHtml = join(serverDistFolder, 'index.server.html');

  const commonEngine = new CommonEngine();

  server.set('view engine', 'html');
  server.set('views', browserDistFolder);

  server.get('*.*', express.static(browserDistFolder, {
    maxAge: '1y'
  }));
  

  server.get('*', (req, res, next) => {
    const { protocol, originalUrl, baseUrl, headers } = req;

    const redirectUrl = redirectMap[req.url];
    if (redirectUrl) {
      res.redirect(301, redirectUrl);
    }else{
      
    commonEngine
    .render({
      bootstrap,
      documentFilePath: indexHtml,
      url: `${protocol}://${headers.host}${originalUrl}`,
      publicPath: browserDistFolder,
      providers: [
        { provide: APP_BASE_HREF, useValue: baseUrl },
      ],
    })
    .then((html) => {
      
      // Extract the state
      const stateRegex = /<script id="ng-state" type="application\/json">(.*?)<\/script>/;
      const match = html.match(stateRegex);
      if (match && match[1]) {
        const state = JSON.parse(match[1]);  
        const sanitizedState = sanitizeState(state);        
        const serializedState = JSON.stringify(sanitizedState);
        // Replace the original state with the sanitized state
        html = html.replace(stateRegex, `<script id="ng-state" type="application/json">${serializedState}</script>`);
      }
      
      res.send(html);
      
    })
    .catch((err) => next(err));
    }

  });

  return server;
}

function run(): void {
  const port = process.env['PORT'] || 4000;

  const server = app();
  server.listen(port, () => {
    console.log(`Node Express server listening on http://localhost:${port}`);
  });
}

run();

redirectMap:

export const redirectMap: { [key: string]: string }  = {
'old url 1' :  'new url 1',
'old url 2' :  'new url 2',
'old url 3' :  'new url 3',
// other urls 
}

sanitize-state :

export function sanitizeState(state: any): any {
    const sanitizedState = JSON.parse(JSON.stringify(state));
  
    Object.keys(sanitizedState).forEach(key => {
      if (sanitizedState[key]?.u) {
        delete sanitizedState[key].u;
      }
    });
  
    return sanitizedState;
  }

Expected behavior
after deployment redirect should work but its not working and same for sanitize state

Versions

  • Angular.js: 18

If you're using the CLI to build
N/A

If you're using file-based installation

[build]
  command = "npm run build"
  publish = "dist/dapps/browser"

[[plugins]]
  package="@netlify/angular-runtime"

[[redirects]]
  from = "/*"
  to = "/index.html"
  status = 200

reason for using redirect in server.ts because redirect is not supported in SSR and i have to redirect more than 3000 urls

@Skn0tt
Copy link
Contributor

Skn0tt commented Jun 11, 2024

Hi @mrrohit1! server.ts is a file auto-scaffolded by Angular and it can be used to run your Angular application e.g. on a VM. It doesn't work on Netlify, though. That also gives some benefits, for example it allows us to serve static assets from a CDN instead of having to use SSR for that.

For your usecase, I'd recommend you to use an Edge Function. Here's a draft for how it could look:

// .netlify/edge-functions/redirect-and-sanitize
import { 
  HTMLRewriter 
} from 'https://ghuc.cc/worker-tools/html-rewriter/index.ts'

export default (request, context) => {
    const redirectUrl = redirectMap[request.url];
    if (redirectUrl) {
      return Response.redirect(redirectUrl, 301);
    }

   const response = await context.next()
   if (!response.headers.get("content-type").startsWith("text/html")) {
      return response
   }


  return new HTMLRewriter()
    .on("#ng-state", {
        element(element) {
            const state = JSON.parse(element.innerHTML);  
            const sanitizedState = sanitizeState(state);        
            element.innerHTML = JSON.stringify(sanitizedState);
        },
    })
    .transform(response)
}

export const config = {
   path: "/*"
}

You'll notice that I replaced your Regex-based approach with HTMLRewriter. That allows us to perform the state sanitisation without blocking the response stream, reducing memory load and latency. Using a Regex should also work fine, though.

Let us know if that helps! Sorry for the confusion with server.ts, we'll add a note the README about it.

@yehor-akunishnikov
Copy link

yehor-akunishnikov commented Jun 16, 2024

Hi, @Skn0tt.
It seems to be impossible to use custom edge functions in case of SSR. In that case, there is a default edge function called 'Angular SSR', that handles server side rendering and has '/*' as a path. In result, that default edge function blocks others from excecution.

Here is my post on the support forum regarding that issue: https://answers.netlify.com/t/netlify-redirects-to-api-server-not-working-on-angular-ssr-website/119988/5. In my case, I wanted to use a custom edge function as a proxy.

Is there any way to make custom edge functions work with SSR, for example, by excluding certain paths?

@Skn0tt
Copy link
Contributor

Skn0tt commented Jun 17, 2024

cc @serhalp

@serhalp
Copy link
Contributor

serhalp commented Jun 18, 2024

@yehor-akunishnikov As my colleague mentioned in reply to your post, I don't believe that's possible currently. However, you should be able to use a custom Netlify Function that runs at the origin, instead of a Netlify Edge Function that runs at the edge. Is that an acceptable workaround for your use case?

@yehor-akunishnikov
Copy link

@serhalp I'll give it a try, thanks.

@daroczig
Copy link

daroczig commented Aug 1, 2024

@serhalp thanks for the above! could you please point me how to set up a Netlify Function to run at the origin? I was not able to find that trigger. would be great if the fn call could be tied to a path prefix as well 🙏

@serhalp
Copy link
Contributor

serhalp commented Aug 1, 2024

@daroczig I could've been clearer! What I meant was that Netlify Functions run at the origin and Netlify Edge Functions run at the edge. So the suggestion here was to use Netlify Functions: https://docs.netlify.com/functions/overview/. That page should have everything you need!

@yehor-akunishnikov
Copy link

yehor-akunishnikov commented Aug 2, 2024

@serhalp thanks for the above! could you please point me how to set up a Netlify Function to run at the origin? I was not able to find that trigger. would be great if the fn call could be tied to a path prefix as well 🙏

@daroczig,
I'll just share my own experience. I tried to use Netlify functions to solve the problem I described earlier. But I ran into the same problem as with the edge functions. In the end, I just gave up and used a VPS instead of Netlify. You may be able to achieve a better result, but for me it's too much trouble for such an obvious task, so it's not worth it.

@daroczig
Copy link

daroczig commented Aug 2, 2024

@serhalp I went through those pages and tried deploying a Netlify Function with a custom path config, but the Angular SSR Edge Function still seems to take priority. Do you have any pointers on what I am missing?

FTR this is what I tried as a POC:

import type { Config, Context } from "@netlify/functions";

export default async (request: Request, context: Context) => {
  console.log(url);
  return new URL("https://reqres.in/api/users?page=2", request.url);
};

export const config: Config = {
  path: "/foobar/*",
};

@serhalp serhalp added the Angular label Aug 9, 2024 — with Linear
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants