extension

package
v0.0.0-...-40ec311 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 18, 2024 License: GPL-3.0 Imports: 11 Imported by: 0

README

Extensions

The GeoIP service is extensible via a Javascript engine. The Javascript API provides a few classes that allow you to manipulate and manage IPs and IP sets.

Directory Structure

The geoip-service app takes a path to the folder containing the extensions. That folder needs to have the following structure.

extensions_folder/
├─ extension_1/
│  ├─ index.js
├─ extension_2/
│  ├─ index.js
│  ...
├─ extension_n/
│  ├─ index.js

Anatomy

Every extension needs to be vanilla Javascript. You can choose to code your extension in Typescript, but ultimatelly it needs to compile down to one file. An example of an extension compiling down to JS can be found here (the building is managed by Parcel). In that repository you'll also find all of the types for Typescript.

All extensions must have an install function that returns the configuration. You could also choose to bootstrap your extension in there.

function install() {
    // Do stuff here.

    return {
        version: 1,
        name: 'your_extensions_name',
        hasLookup: true,
        endpoints: [...],
        jobs: [...],
    };
}

The configuration is very straightforward.

  • name: This is mandatory and should not have any spaces.
  • version: This is not used currently, but will be in the future.
  • hasLookup: This field is optional and if set to true signifies if the extension intercepts an IP lookup.
  • endpoints: This is an array which contains all defined endpoints (see below).
  • jobs: This is an array which contains all defined jobs (see further down).
Endpoints

An extension could expose any amount of endpoints. The configuration for each endpoint looks as follows:

{
    method: 'GET', // Or POST, PUT, DELETE.
    handler: 'functionName',
    endpoint: '/path/to/endpoint/:param',
}

The endpoint should be defined exactly as you would define a route in Gin. The handler member should contain the exact name of the function you intend to call when the endpoint is hit. These functions look something like this:

const functionName = (req, res) => {
    const myParam = req.param('param');

    if (isNotWhatIExpect(myParam)) {
        res.abort(500);
        return;
    }

    res.json(200, queryResult(myParam));
}

The type definitions will give you an idea of what is available on both req and res.

Jobs

The configuration also allows you to add any number of cron jobs. Let's take a look at the configuration for each job.

{
    cron: '0 */2 * * *',
    job: 'jobFunctionName',
}

In this case we've defined a cron job which runs the function jobFunctionName every two hours. The function is a simple one:

const jobFunctionName = () => {
    // Do stuff here.
};

Your cron jobs can be used to pull data from IP lists on the web, or whatever you need.

hasLookup

The hasLookup option refers to whether the extension can perform IP lookups. However, in order for it to work you also need to define the lookupIP function. It takes in one argument, the IP address as a string, and you can return whatever you want. In the example below the function returns an object. The result of this lookup will be added to the additional_information field of the IP lookup object.

function lookupIP(ip) {
    const details = getIPDetails(ip);

    return {
        appears_in_list: !!details,
        details,
    };
}

Javascript APIs

Coming soon.

Full Example

const IP_LIST_DATA = 'https://some.org/some_ip_list.ipset';
const DATA_FILE = 'data.json';
let ipList = [];

const listHasIP = (ip) => {
    for (let i = 0; i < ipList.length; i++) {
        if (ipList[i].contains(ip)) {
            return true;
        }
    }

    return false;
};

const lookupIP = (ip) => {
    if (listHasIP(ip)) {
        // This data will appear as `additional_info` in the API response.
        return {
            info: 'This IP was marked as XXXX',
            list: IP_LIST_DATA,
            tag: 'XXXX',
        };
    }
};

const refreshData = () => {
    // Run `fetch` to get the data and then `storage.save` to save the updated version.
};

// If you define `hasLookup` in the config then you need a function with this name.
const loadData = async () => {
    await storage.init();

    try {
        const json = await storage.read(DATA_FILE);
        const data = JSON.parse(json);

        data.forEach((ip) => {
            ipList.push(new IP(ip));
        });
    } catch (e) {
        console.log('Error:', e);
    }
};

const install = () => {
    loadData();

    return {
        version: 1,
        hasLookup: true,
        jobs: [{
            // A cron that runs every hour.
            cron: '0 * * * *',
            job: 'refreshData',
        }],
    };
};

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type CronJob

type CronJob struct {
	Cron string `json:"cron"`
	Job  string `json:"job"`
}

type EndpointDetails

type EndpointDetails struct {
	Endpoint string `json:"endpoint"`
	Method   string `json:"method"`
	Handler  string `json:"handler"`
}

type EndpointReq

type EndpointReq struct {
	Param     func(key string) string `json:"param"`
	GetHeader func(key string) string `json:"getHeader"`
	GetQuery  func(key string) string `json:"getQuery"`
}

type EndpointRes

type EndpointRes struct {
	JSON  func(status int, resp any)          `json:"json"`
	Abort func(status int, err string)        `json:"abort"`
	Send  func(status int, mime, resp string) `json:"send"`
	HTML  func(status int, html string)       `json:"html"`
}

type Extension

type Extension struct {
	ExtDir string
	Dir    os.DirEntry
	Entry  os.DirEntry
	// contains filtered or unexported fields
}

func (*Extension) Init

func (e *Extension) Init() error

Init will spin up the JS VM and run the script.

func (*Extension) IsEndpointExtension

func (e *Extension) IsEndpointExtension() bool

IsEndpointExtension returns true if this extension defines an endpoint.

func (*Extension) IsLookupExtension

func (e *Extension) IsLookupExtension() bool

IsLookupExtension returns true if this extension has lookup functions and should run on IP or domain lookup.

func (*Extension) RegisterEndpoints

func (e *Extension) RegisterEndpoints(r *gin.Engine) bool

RegisterEndpoints will go through all of the endpoints and register them with gin.

func (*Extension) RunIPLookup

func (e *Extension) RunIPLookup(ip string) (any, error)

RunIPLookup will query the extension for data on a particular IP address.

type ExtensionConfig

type ExtensionConfig struct {
	Version   int               `json:"version"`
	HasLookup bool              `json:"hasLookup"`
	Endpoints []EndpointDetails `json:"endpoints"`
	Jobs      []CronJob         `json:"jobs"`
	Name      string            `json:"name"`
}

type HandlerFn

type HandlerFn func(req EndpointReq, res EndpointRes)

type InstallFn

type InstallFn func() ExtensionConfig

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL