From 54cfd4c3532b6383c5340b977cc50b7797b48075 Mon Sep 17 00:00:00 2001 From: mariobassem Date: Sun, 2 Feb 2025 18:04:00 +0200 Subject: [PATCH] feat: add ip-api client - Add a new ip-api client to the project. - This client uses the ip-api.com API to get IP information. - An example is provided in `examples/develop/ipapi`. Co-authored-by: mahmmoud.hassanein --- examples/develop/ipapi/example.vsh | 8 +++ lib/clients/ipapi/.heroscript | 8 +++ lib/clients/ipapi/client.v | 29 ++++++++ lib/clients/ipapi/ipapi_factory_.v | 102 +++++++++++++++++++++++++++++ lib/clients/ipapi/ipapi_model.v | 58 ++++++++++++++++ lib/clients/ipapi/readme.md | 30 +++++++++ 6 files changed, 235 insertions(+) create mode 100755 examples/develop/ipapi/example.vsh create mode 100644 lib/clients/ipapi/.heroscript create mode 100644 lib/clients/ipapi/client.v create mode 100644 lib/clients/ipapi/ipapi_factory_.v create mode 100644 lib/clients/ipapi/ipapi_model.v create mode 100644 lib/clients/ipapi/readme.md diff --git a/examples/develop/ipapi/example.vsh b/examples/develop/ipapi/example.vsh new file mode 100755 index 00000000..b261eda8 --- /dev/null +++ b/examples/develop/ipapi/example.vsh @@ -0,0 +1,8 @@ +#!/usr/bin/env -S v -n -w -gc none -no-retry-compilation -d use_openssl -enable-globals run + +import freeflowuniverse.herolib.clients.ipapi +import os + +mut ip_api_client := ipapi.get()! +info := ip_api_client.get_ip_info('37.27.132.46')! +println('info: ${info}') diff --git a/lib/clients/ipapi/.heroscript b/lib/clients/ipapi/.heroscript new file mode 100644 index 00000000..0ef05283 --- /dev/null +++ b/lib/clients/ipapi/.heroscript @@ -0,0 +1,8 @@ + +!!hero_code.generate_client + name:'ipapi' + classname:'IPApi' + singleton:0 + default:1 + hasconfig:1 + reset:0 \ No newline at end of file diff --git a/lib/clients/ipapi/client.v b/lib/clients/ipapi/client.v new file mode 100644 index 00000000..7f7bd45a --- /dev/null +++ b/lib/clients/ipapi/client.v @@ -0,0 +1,29 @@ +module ipapi + +import json + +pub struct IPInfo { +pub: + query string + status string + country string + country_code string @[json: 'countryCode'] + region string + region_name string @[json: 'regionName'] + city string + zip string + lat f32 + lon f32 + timezone string + isp string + org string + as string +} + +pub fn (mut a IPApi) get_ip_info(ip string) !IPInfo { + mut conn := a.connection()! + res := conn.get_json(prefix: 'json/${ip}')! + info := json.decode(IPInfo, res)! + + return info +} diff --git a/lib/clients/ipapi/ipapi_factory_.v b/lib/clients/ipapi/ipapi_factory_.v new file mode 100644 index 00000000..29ec0926 --- /dev/null +++ b/lib/clients/ipapi/ipapi_factory_.v @@ -0,0 +1,102 @@ +module ipapi + +import freeflowuniverse.herolib.core.base +import freeflowuniverse.herolib.core.playbook +import freeflowuniverse.herolib.ui.console + +__global ( + ipapi_global map[string]&IPApi + ipapi_default string +) + +/////////FACTORY + +@[params] +pub struct ArgsGet { +pub mut: + name string +} + +fn args_get(args_ ArgsGet) ArgsGet { + mut args := args_ + if args.name == '' { + args.name = ipapi_default + } + if args.name == '' { + args.name = 'default' + } + return args +} + +pub fn get(args_ ArgsGet) !&IPApi { + mut args := args_get(args_) + if args.name !in ipapi_global { + if args.name == 'default' { + if !config_exists(args) { + if default { + config_save(args)! + } + } + config_load(args)! + } + } + return ipapi_global[args.name] or { + println(ipapi_global) + panic('could not get config for ipapi with name:${args.name}') + } +} + +fn config_exists(args_ ArgsGet) bool { + mut args := args_get(args_) + mut context := base.context() or { panic('bug') } + return context.hero_config_exists('ipapi', args.name) +} + +fn config_load(args_ ArgsGet) ! { + mut args := args_get(args_) + mut context := base.context()! + mut heroscript := context.hero_config_get('ipapi', args.name)! + play(heroscript: heroscript)! +} + +fn config_save(args_ ArgsGet) ! { + mut args := args_get(args_) + mut context := base.context()! + context.hero_config_set('ipapi', args.name, heroscript_default()!)! +} + +fn set(o IPApi) ! { + mut o2 := obj_init(o)! + ipapi_global[o.name] = &o2 + ipapi_default = o.name +} + +@[params] +pub struct PlayArgs { +pub mut: + heroscript string // if filled in then plbook will be made out of it + plbook ?playbook.PlayBook + reset bool +} + +pub fn play(args_ PlayArgs) ! { + mut args := args_ + + if args.heroscript == '' { + args.heroscript = heroscript_default()! + } + mut plbook := args.plbook or { playbook.new(text: args.heroscript)! } + + mut install_actions := plbook.find(filter: 'ipapi.configure')! + if install_actions.len > 0 { + for install_action in install_actions { + mut p := install_action.params + cfg_play(p)! + } + } +} + +// switch instance to be used for ipapi +pub fn switch(name string) { + ipapi_default = name +} diff --git a/lib/clients/ipapi/ipapi_model.v b/lib/clients/ipapi/ipapi_model.v new file mode 100644 index 00000000..2f8392db --- /dev/null +++ b/lib/clients/ipapi/ipapi_model.v @@ -0,0 +1,58 @@ +module ipapi + +import freeflowuniverse.herolib.data.paramsparser +import freeflowuniverse.herolib.core.httpconnection +import os + +pub const version = '1.14.3' +const singleton = false +const default = true + +// TODO: THIS IS EXAMPLE CODE AND NEEDS TO BE CHANGED IN LINE TO STRUCT BELOW, IS STRUCTURED AS HEROSCRIPT +pub fn heroscript_default() !string { + heroscript := " + !!ipapi.configure + name:'default' + " + + return heroscript +} + +// THIS THE THE SOURCE OF THE INFORMATION OF THIS FILE, HERE WE HAVE THE CONFIG OBJECT CONFIGURED AND MODELLED + +@[heap] +pub struct IPApi { +pub mut: + name string = 'default' + + conn ?&httpconnection.HTTPConnection @[str: skip] +} + +fn cfg_play(p paramsparser.Params) ! { + // THIS IS EXAMPLE CODE AND NEEDS TO BE CHANGED IN LINE WITH struct above + mut mycfg := IPApi{ + name: p.get_default('name', 'default')! + } + set(mycfg)! +} + +fn obj_init(obj_ IPApi) !IPApi { + // never call get here, only thing we can do here is work on object itself + mut obj := obj_ + return obj +} + +pub fn (mut client IPApi) connection() !&httpconnection.HTTPConnection { + mut c := client.conn or { + mut c2 := httpconnection.new( + name: 'ipapi_${client.name}' + url: 'http://ip-api.com' + cache: false + retry: 20 + )! + c2 + } + + client.conn = c + return c +} diff --git a/lib/clients/ipapi/readme.md b/lib/clients/ipapi/readme.md new file mode 100644 index 00000000..01288e93 --- /dev/null +++ b/lib/clients/ipapi/readme.md @@ -0,0 +1,30 @@ +# ipapi + + + +To get started + +```vlang + + +import freeflowuniverse.herolib.clients. ipapi + +mut client:= ipapi.get()! + +client... + + + + +``` + +## example heroscript + +```hero +!!ipapi.configure + secret: '...' + host: 'localhost' + port: 8888 +``` + +