Coder Social home page Coder Social logo

Proxy support about servor HOT 10 OPEN

lukejacksonn avatar lukejacksonn commented on May 14, 2024 4
Proxy support

from servor.

Comments (10)

lukejacksonn avatar lukejacksonn commented on May 14, 2024 6

I'm going to work on this along with some other PRs and issues. It might make it into the next release.. not 100% on it yet though. I think it will rely mostly on implementing the picking up of a servor config file so that we don't have to keep adding cli flags.

from servor.

lukejacksonn avatar lukejacksonn commented on May 14, 2024 5

Thanks @thisguychris that does make things easier to comprehend. So essentially you want to reserve a specific route to make a request to another specified server with the same round sans the /api? Something like:

if (url.match(config.proxy.path)) {
  fetch(config.proxy.destination + url.replace(config.proxy.path, ''))
    .then(json => res.json())
    .then(json => res.send(json))
}

But probably as a raw stream rather than parsed json or whatever?

from servor.

lukejacksonn avatar lukejacksonn commented on May 14, 2024 1

Hey all 👋 servor does not currently support this behaviour. It is really meant to be a local replica of a static CDN like server (albeit with a few handy development features). I have never used servor in production rather I push my sites/apps I develop with it to something like Netlify or GitHub Pages.

That said, servor does have a node API which you could use to combine it with another server such as express for example. That kind of defeats the purpose though I guess.

Have you got an example of how the API might look like if we were to include such functionality in servor, how would you want to configure it etc?

from servor.

analytik avatar analytik commented on May 14, 2024

Hi. I have a similar use case. I already have a fairly simple Express instance that both serves html/js/css files and API requests. What I'd like to do is if env === dev, then serve static files and fallback through servor.

from servor.

analytik avatar analytik commented on May 14, 2024

Thanks for the fast response! I'll see if I can make a prototype and see what I really want to do. I guess for me, the functionality I'm looking for is live reload. This is probably not a universal problem, but my setup is basically mixing node.js urls with a html5 app. Sorry to subhero24 for derailing his topic maybe - in my case I am OK with serving Vue and API from the same server, although obviously ideally there would be different microservices for those.

My router is currently like

const {join} = require("path");

module.exports = function (express, router, debugController) {
    return function (app, rootPath) {
        debugController.setRouting(router);
        router.get('/', function (req, res) {
            return res.sendFile(`${rootPath}/site/index.html`);
        });
        router.get('/static/:page', function (req, res) {
            return res.sendFile(`${rootPath}/site/${req.params.page}.html`);
        });
        for (const route of getVueRoutes()) {
            // console.log(`-------------VUE- ${route} ---------`);
            router.get(route, function (req, res) {
                return res.sendFile(`${rootPath}/site/index.html`);
            });
        }
        app.use(router);
        app.use(express.static(join(rootPath, "site")));
    }
};

function getVueRoutes() {
    const requireModule = require("esm")(module);
    const routes = requireModule('../site/routes.esm.js');
    // console.dir(routes.default.routes);
    return routes.default.routes.map(vueRouteObject => vueRouteObject.path).filter(foo => foo !== '*');
}

Here express is a configured express instance, router is an express-promise-router and debugController is just a controller, where setRoutes adds a few API URLs. For root, and for Vue routes I serve the main vue app, index.html (ideally here would be some clever isomorphic functionality, but whatever).

Then I can serve some plain-html static files, and everything else (css, js, assets) I serve from the 'site' folder (invisible to browsers).

This is pretty clunky, but it does what I need. Mostly I want to see how far I can go with buildless Vue while compensating for some of the lost benefits of a build process. If I manage to make a usable snippet, I'll share it here today, or during the weekend.

from servor.

analytik avatar analytik commented on May 14, 2024

OK, that wasn't actually that hard. Not sure if this helps anyone, but... shrug :D I tried.

Of course for this to be sharable, it would need wrapping to a neat configurable module, basically passing along the router object, base path, index file path, etc.

To actually comment to the original question by @subhero24 :

  • if you want to serve Vue + API from one Node process, you can use something like the first following code snippet
  • if you want to proxy things from Servor, I guess the fastes would be to fork it and add something like the second code snippet (this one would work in Express, probably needs adjustments for plain http server)

Please note that fs in this case is really bluebird.promisifyAll(require('fs')).

const {join} = require('path');
const fs = require('../container.js').get('fs');
const clients = [];

module.exports = function (express, router, debugController) {
    return function (app, rootPath) {
        debugController.setRouting(router);
        router.get('/', serveIndex(rootPath));
        router.get('/static/:page', function (req, res) {
            return res.sendFile(`${rootPath}/site/${req.params.page}.html`);
        });
        router.get('/livereload', liveReloadController);
        for (const route of getVueRoutes()) {
            // console.log(`-------------VUE- ${route} ---------`);
            router.get(route, serveIndex(rootPath));
        }
        app.use(router);
        app.use(express.static(join(rootPath, 'site')));
        if (process.env.ENV === 'dev' || process.env.NODE_ENV === 'development') {
            console.log('Starting up file watcher for Servor.');
            watchChanges(join(rootPath, 'site'));
        }
    }
};

function getVueRoutes() {
    const requireModule = require('esm')(module);
    const routes = requireModule('../site/routes.esm.js');
    // console.dir(routes.default.routes);
    return routes.default.routes.map(vueRouteObject => vueRouteObject.path).filter(foo => foo !== '*');
}

// servor stuff from here on:

const liveReloadScript = `<head>
  <script>
    const source = new EventSource('/livereload');
    source.onmessage = e => location.reload(true);
    console.log('[servor-express] listening for file changes');
  </script>
`;

function serveIndex(rootPath) {
    if (process.env.ENV === 'dev' || process.env.NODE_ENV === 'development') {
        return async function (req, res) {
            let content = await fs.readFileAsync(`${rootPath}/site/index.html`, 'utf8');
            content = content.replace('<head>', liveReloadScript);
            return res.send(content).end();
        }
    }
    return function (req, res) {
        return res.sendFile(`${rootPath}/site/index.html`);
    }
}

function sendMessage (res, channel, data) {
    res.write(`event: ${channel}\nid: 0\ndata: ${data}\n`);
    res.write('\n\n');
}

function watchChanges(path) {
    fs.watch(path, {recursive: true}, () => {
        while (clients.length > 0) {
            sendMessage(clients.pop(), 'message', 'reload');
        }
    });
}

function liveReloadController(req, res) {
    res.writeHead(200, {
        Connection: 'keep-alive',
        'Content-Type': 'text/event-stream',
        'Cache-Control': 'no-cache',
        'Access-Control-Allow-Origin': '*'
    });
    sendMessage(res, 'connected', 'ready');
    setInterval(sendMessage, 60000, res, 'ping', 'waiting');
    clients.push(res);
}

how 2 proxy:

// rp = requst-promise
async function proxy(req, res) {
    try {
        const resp = await rp({
            url: `http://127.0.0.1:80${req.path}`,
            method: req.method,
            resolveWithFullResponse: true,
            headers: req.headers,
            simple: false
        });
        return res
            .append('content-type', resp.headers['content-type'] || 'application/json; charset=utf-8')
            .send(resp.body)
            .status(resp.statusCode)
            .end();
    }
    catch (err) {
        console.error(err);
        return res
            .send(err && err.body || 'Server error')
            .status(err && err.statusCode || 500)
            .end();
    }
}

from servor.

thisguychris avatar thisguychris commented on May 14, 2024

Hi @lukejacksonn 👋🏻

I'm just dropping here to support this issue and add a use case. Ideally, you should just drop files in a CDN. In a microservice world, it's better to have a relative URL, as opposed to using it externally, because you skip the whole tango dance with CORs. And let's be honest, CORs is just a pain to configure 🤣.

We've been using this approach for years now, and hooking sites to Cloudflare, makes websites housed in Nginx served in a CDN fashion.

Ideally, the API would look like this, if we're using a config. If we are to use the cli, I propose we use -P like what http-server does.

{
  "proxy": [
    { "path": "api/**", "destination": "https://xxx.api/" }
  ]
}

With the rise of browsers blocking third-party scripts by default. I think this approach would be more popular.

And I think this is going to be a norm, Netlify, Firebase, Zeit, Cloudflare all have a convenient way to proxy a relative API to a microservice/external URL. And they're all housed by a CDN.

from servor.

thisguychris avatar thisguychris commented on May 14, 2024

@lukejacksonn yes! something like that 😊

from servor.

subhero24 avatar subhero24 commented on May 14, 2024

@lukejacksonn that would be perfect!

from servor.

danbroooks avatar danbroooks commented on May 14, 2024

@lukejacksonn +1 for this

from servor.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.