Bump to 6.0.43, use go generate

Also moved fetching of JSON information to Go for cross-OS usage.
This commit is contained in:
Paul Tyng
2021-04-09 09:41:43 -04:00
parent 5219e06753
commit 4ab4036985
76 changed files with 504 additions and 429 deletions

View File

@@ -10,6 +10,8 @@ Many of the naming adjustments are breaking changes, but to simplify things, tre
The data models and basic REST methods are "generated" from JSON files in the JAR that show all fields and the associated regex/validation information.
This is kind of gross, I wanted to switch to using the java classes in the jar like scala2go but the jar is obfuscated and I couldn't find a way to extract that information from anywhere else. Maybe it exists somewhere in the web UI, but I was unable to find it in there in a way that was extractable in a practical way.
To regenerate the code, you can bump the Unifi Controller version number in [unifi/gen.go] and run `go generate` inside the `unifi` directory.
This code generation is kind of gross, I wanted to switch to using the java classes in the jar like scala2go but the jar is obfuscated and I couldn't find a way to extract that information from anywhere else. Maybe it exists somewhere in the web UI, but I was unable to find it in there in a way that was extractable in a practical way.
Still planning to dig through the bits some more later on.

View File

@@ -1,3 +0,0 @@
{
"name":".{1,128}"
}

View File

@@ -1,4 +0,0 @@
{
"name": "",
"hostname": ""
}

View File

@@ -29,7 +29,7 @@
"tx_power_mode": "auto|medium|high|low|custom",
"tx_power": "[\\d]+|auto",
"min_rssi_enabled": "true|false",
"min_rssi": "^-([1-9]|[1-8][0-9]|9[0-4])$",
"min_rssi": "^-(6[7-9]|[7-8][0-9]|90)$",
"name": "",
"hard_noise_floor_enabled": "true|false",
"sens_level_enabled": "true|false",

View File

@@ -1,6 +1,6 @@
{
"enabled": "true|false",
"server": "",
"outbound_proxy": "",
"package_url": ""
"package_url": "",
"server": ""
}

View File

@@ -1,8 +1,8 @@
{
"sound_before_enabled": "true|false",
"sound_before_type": "sample|media",
"sound_before_resource": "",
"sound_after_enabled": "true|false",
"sound_after_resource": "",
"sound_after_type": "sample|media",
"sound_after_resource": ""
"sound_before_enabled": "true|false",
"sound_before_resource": "",
"sound_before_type": "sample|media"
}

View File

@@ -1,8 +1,8 @@
{
"enable_isolated_wlan": "true|false",
"enabled": "true|false",
"x_mesh_essid": "",
"x_mesh_psk": "",
"uplink_host": "",
"uplink_type": ""
"uplink_type": "",
"x_mesh_essid": "",
"x_mesh_psk": ""
}

View File

@@ -1,95 +1,95 @@
{
"portal_enabled": "true|false",
"allowed_subnet_": "",
"auth": "none|hotspot|facebook_wifi|custom",
"redirect_enabled": "true|false",
"redirect_url": "",
"redirect_https": "true|false",
"redirect_to_https": "true|false",
"portal_customized": "true|false",
"portal_use_hostname": "true|false",
"portal_hostname": "^[a-zA-Z0-9.-]+$|^$",
"authorize_use_sandbox": "true|false",
"custom_ip": "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^$",
"ec_enabled": "true|false",
"expire": "[\\d]+|custom",
"expire_number": "^[1-9][0-9]{0,5}|1000000$",
"expire_unit": "1|60|1440",
"x_password": "",
"custom_ip": "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^$",
"facebook_enabled": "true|false",
"facebook_app_id": "",
"x_facebook_app_secret": "",
"facebook_enabled": "true|false",
"facebook_scope_email": "true|false",
"facebook_wifi_block_https": "true|false",
"facebook_wifi_gw_id": "",
"facebook_wifi_gw_name": "",
"x_facebook_wifi_gw_secret": "",
"facebook_wifi_block_https": "true|false",
"google_enabled": "true|false",
"google_client_id": "",
"x_google_client_secret": "",
"google_domain": "",
"google_scope_email": "true|false",
"password_enabled": "true|false",
"voucher_enabled": "true|false",
"voucher_customized": "true|false",
"payment_enabled": "true|false",
"restricted_subnet_": "",
"allowed_subnet_": "",
"restricted_dns_enabled": "false|true",
"restricted_dns_servers": [
"^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^$"
],
"gateway": "paypal|stripe|authorize|quickpay|merchantwarrior|ippay",
"paypal_use_sandbox": "true|false",
"x_paypal_username": "",
"x_paypal_password": "",
"x_paypal_signature": "",
"x_stripe_api_key": "",
"x_quickpay_agreementid": "",
"x_quickpay_apikey": "",
"x_quickpay_merchantid": "",
"quickpay_testmode": "true|false",
"x_authorize_loginid": "",
"x_authorize_transactionkey": "",
"x_merchantwarrior_merchantuuid": "",
"x_merchantwarrior_apikey": "",
"x_merchantwarrior_apipassphrase": "",
"x_ippay_terminalid": "",
"merchantwarrior_use_sandbox": "false|true",
"authorize_use_sandbox": "true|false",
"google_client_id": "",
"google_domain": "",
"google_enabled": "true|false",
"google_scope_email": "true|false",
"ippay_use_sandbox": "true|false",
"template_engine": "jsp|angular",
"merchantwarrior_use_sandbox": "false|true",
"password_enabled": "true|false",
"payment_enabled": "true|false",
"paypal_use_sandbox": "true|false",
"portal_customized": "true|false",
"portal_customized_bg_color": "^#[a-zA-Z0-9]{6}$|^#[a-zA-Z0-9]{3}$|^$",
"portal_customized_bg_image_enabled": "true|false",
"portal_customized_bg_image_filename": "",
"portal_customized_bg_image_tile": "true|false",
"portal_customized_box_color": "^#[a-zA-Z0-9]{6}$|^#[a-zA-Z0-9]{3}$|^$",
"portal_customized_box_link_color": "^#[a-zA-Z0-9]{6}$|^#[a-zA-Z0-9]{3}$|^$",
"portal_customized_box_opacity": "^[1-9][0-9]?$|^100$|^$",
"portal_customized_box_text_color": "^#[a-zA-Z0-9]{6}$|^#[a-zA-Z0-9]{3}$|^$",
"portal_customized_button_color": "^#[a-zA-Z0-9]{6}$|^#[a-zA-Z0-9]{3}$|^$",
"portal_customized_button_text_color": "^#[a-zA-Z0-9]{6}$|^#[a-zA-Z0-9]{3}$|^$",
"portal_customized_languages": [
"^[a-z]{2}(_[A-Z]{2})*$"
],
"portal_customized_link_color": "^#[a-zA-Z0-9]{6}$|^#[a-zA-Z0-9]{3}$|^$",
"portal_customized_logo_enabled": "true|false",
"portal_customized_logo_filename": "",
"portal_customized_text_color": "^#[a-zA-Z0-9]{6}$|^#[a-zA-Z0-9]{3}$|^$",
"portal_customized_title": "",
"portal_customized_tos_enabled": "true|false",
"portal_customized_tos": "",
"portal_customized_tos_enabled": "true|false",
"portal_customized_unsplash_author_name": "",
"portal_customized_unsplash_author_username": "",
"portal_customized_welcome_text": "",
"portal_customized_welcome_text_enabled": "true|false",
"portal_customized_welcome_text_position": "under_logo|above_boxes",
"portal_customized_box_link_color": "^#[a-zA-Z0-9]{6}$|^#[a-zA-Z0-9]{3}$|^$",
"portal_customized_box_text_color": "^#[a-zA-Z0-9]{6}$|^#[a-zA-Z0-9]{3}$|^$",
"portal_customized_languages": [
"^[a-z]{2}(_[A-Z]{2})*$"
],
"radius_enabled": "true|false",
"portal_enabled": "true|false",
"portal_hostname": "^[a-zA-Z0-9.-]+$|^$",
"portal_use_hostname": "true|false",
"quickpay_testmode": "true|false",
"radius_auth_type": "chap|mschapv2",
"radius_disconnect_enabled": "true|false",
"radius_disconnect_port": "[1-9][0-9]{0,3}|[1-5][0-9]{4}|[6][0-4][0-9]{3}|[6][5][0-4][0-9]{2}|[6][5][5][0-2][0-9]|[6][5][5][3][0-5]",
"radius_enabled": "true|false",
"radiusprofile_id": "",
"wechat_enabled": "true|false",
"redirect_enabled": "true|false",
"redirect_https": "true|false",
"redirect_to_https": "true|false",
"redirect_url": "",
"restricted_dns_enabled": "false|true",
"restricted_dns_servers": [
"^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^$"
],
"restricted_subnet_": "",
"template_engine": "jsp|angular",
"voucher_customized": "true|false",
"voucher_enabled": "true|false",
"wechat_app_id": "",
"wechat_enabled": "true|false",
"wechat_shop_id": "",
"x_authorize_loginid": "",
"x_authorize_transactionkey": "",
"x_facebook_app_secret": "",
"x_facebook_wifi_gw_secret": "",
"x_google_client_secret": "",
"x_ippay_terminalid": "",
"x_merchantwarrior_apikey": "",
"x_merchantwarrior_apipassphrase": "",
"x_merchantwarrior_merchantuuid": "",
"x_password": "",
"x_paypal_password": "",
"x_paypal_signature": "",
"x_paypal_username": "",
"x_quickpay_agreementid": "",
"x_quickpay_apikey": "",
"x_quickpay_merchantid": "",
"x_stripe_api_key": "",
"x_wechat_app_secret": "",
"x_wechat_secret_key": "",
"ec_enabled": "true|false"
"x_wechat_secret_key": ""
}

View File

@@ -1,12 +1,39 @@
{
"dns_filtering": "true|false",
"dns_filters": [
{
"allowed_sites": [
"^[a-zA-Z0-9.-]+$|^$"
],
"blocked_sites": [
"^[a-zA-Z0-9.-]+$|^$"
],
"blocked_tld": [
"^[a-zA-Z0-9.-]+$|^$"
],
"description": "",
"filter": "none|work|family",
"name": "",
"network_id": "",
"version": "v4|v6"
}
],
"enabled_categories": [
"emerging-activex|emerging-attackresponse|botcc|emerging-chat|ciarmy|compromised|emerging-dns|emerging-dos|dshield|emerging-exploit|emerging-ftp|emerging-games|emerging-icmp|emerging-icmpinfo|emerging-imap|emerging-inappropriate|emerging-info|emerging-malware|emerging-misc|emerging-mobile|emerging-netbios|emerging-p2p|emerging-policy|emerging-pop3|emerging-rpc|emerging-scada|emerging-scan|emerging-shellcode|emerging-smtp|emerging-snmp|emerging-sql|emerging-telnet|emerging-tftp|tor|emerging-trojan|emerging-useragent|emerging-voip|emerging-webapps|emerging-webclient|emerging-webserver|emerging-worm"
],
"endpoint_scanning": "true|false",
"honeypot": [
{
"ip_address": "",
"network_id": "",
"version": "v4|v6"
}
],
"honeypot_enabled": "true|false",
"ips_mode": "ids|ips|ipsInline|disabled",
"restrict_ip_addresses": "true|false",
"restrict_tor": "true|false",
"restrict_torrents": "true|false",
"restrict_ip_addresses": "true|false",
"suppression": {
"alerts": [
{
@@ -31,32 +58,5 @@
"value": ""
}
]
},
"dns_filtering": "true|false",
"dns_filters": [
{
"name": "",
"description": "",
"filter": "none|work|family",
"network_id": "",
"version": "v4|v6",
"allowed_sites": [
"^[a-zA-Z0-9.-]+$|^$"
],
"blocked_sites": [
"^[a-zA-Z0-9.-]+$|^$"
],
"blocked_tld": [
"^[a-zA-Z0-9.-]+$|^$"
]
}
],
"honeypot_enabled": "true|false",
"honeypot": [
{
"network_id": "",
"ip_address": "",
"version": "v4|v6"
}
]
}
}

View File

@@ -1,7 +1,7 @@
{
"enabled": "true|false",
"sync": "true|false",
"touch_event": "true|false",
"brightness": "[1-9]|[1-9][0-9]|100",
"idle_timeout": "[1-9][0-9]|[1-9][0-9][0-9]|[1-2][0-9][0-9][0-9]|3[0-5][0-9][0-9]|3600"
"enabled": "true|false",
"idle_timeout": "[1-9][0-9]|[1-9][0-9][0-9]|[1-2][0-9][0-9][0-9]|3[0-5][0-9][0-9]|3600",
"sync": "true|false",
"touch_event": "true|false"
}

View File

@@ -1,21 +1,21 @@
{
"advanced_feature_enabled": "true|false",
"alert_enabled": "true|false",
"auto_upgrade": "true|false",
"boot_sound": "true|false",
"led_enabled": "true|false",
"advanced_feature_enabled": "true|false",
"outdoor_mode_enabled": "true|false",
"unifi_idp_enabled": "true|false",
"wifiman_enabled": "true|false",
"x_mgmt_key": "[0-9a-f]{32}",
"x_ssh_auth_password_enabled": "true|false",
"x_ssh_bind_wildcard": "true|false",
"x_ssh_enabled": "true|false",
"x_ssh_keys": [
""
],
"x_ssh_username": "^[_A-Za-z0-9][-_.A-Za-z0-9]{0,29}$",
"x_ssh_password": ".{1,128}",
"x_ssh_md5passwd": "",
"x_ssh_password": ".{1,128}",
"x_ssh_sha512passwd": "",
"x_ssh_enabled": "true|false",
"x_ssh_bind_wildcard": "true|false",
"x_ssh_auth_password_enabled": "true|false"
"x_ssh_username": "^[_A-Za-z0-9][-_.A-Za-z0-9]{0,29}$"
}

View File

@@ -1,5 +1,5 @@
{
"enabled": "false|true",
"download": "^[1-9][0-9]*$",
"enabled": "false|true",
"upload": "^[1-9][0-9]*$"
}

View File

@@ -1,27 +1,27 @@
{
"enabled": "true|false",
"cron_expr": "",
"default": "true|false",
"radios": [
"na|ng"
],
"optimize": [
"channel|power"
"channels_na": [
"36|40|44|48|52|56|60|64|100|104|108|112|116|120|124|128|132|136|140|144|149|153|157|161|165"
],
"channels_ng": [
"1|2|3|4|5|6|7|8|9|10|11|12|13|14"
],
"channels_na": [
"36|40|44|48|52|56|60|64|100|104|108|112|116|120|124|128|132|136|140|144|149|153|157|161|165"
],
"ht_modes_ng": [
"^(20|40)$"
"cron_expr": "",
"default": "true|false",
"enabled": "true|false",
"exclude_devices": [
"([0-9a-z]{2}:){5}[0-9a-z]{2}"
],
"ht_modes_na": [
"^(20|40|80|160)$"
],
"exclude_devices": [
"([0-9a-z]{2}:){5}[0-9a-z]{2}"
"ht_modes_ng": [
"^(20|40)$"
],
"optimize": [
"channel|power"
],
"radios": [
"na|ng"
],
"useXY": "true|false"
}

View File

@@ -1,10 +1,10 @@
{
"enabled": "true|false",
"auth_port": "[1-9][0-9]{0,3}|[1-5][0-9]{4}|[6][0-4][0-9]{3}|[6][5][0-4][0-9]{2}|[6][5][5][0-2][0-9]|[6][5][5][3][0-5]",
"acct_port": "[1-9][0-9]{0,3}|[1-5][0-9]{4}|[6][0-4][0-9]{3}|[6][5][0-4][0-9]{2}|[6][5][5][0-2][0-9]|[6][5][5][3][0-5]",
"configure_whole_network": "true|false",
"tunneled_reply": "true|false",
"interim_update_interval": "^([6-9][0-9]|[1-9][0-9]{2,3}|[1-7][0-9]{4}|8[0-5][0-9]{3}|86[0-3][0-9][0-9]|86400)$",
"accounting_enabled": "true|false",
"acct_port": "[1-9][0-9]{0,3}|[1-5][0-9]{4}|[6][0-4][0-9]{3}|[6][5][0-4][0-9]{2}|[6][5][5][0-2][0-9]|[6][5][5][3][0-5]",
"auth_port": "[1-9][0-9]{0,3}|[1-5][0-9]{4}|[6][0-4][0-9]{3}|[6][5][0-4][0-9]{2}|[6][5][5][0-2][0-9]|[6][5][5][3][0-5]",
"configure_whole_network": "true|false",
"enabled": "true|false",
"interim_update_interval": "^([6-9][0-9]|[1-9][0-9]{2,3}|[1-7][0-9]{4}|8[0-5][0-9]{3}|86[0-3][0-9][0-9]|86400)$",
"tunneled_reply": "true|false",
"x_secret": "[^\\\"\\' ]{1,48}"
}

View File

@@ -1,11 +1,11 @@
{
"debug": "false|true",
"enabled": "true|false",
"ip": "",
"port": "[1-9][0-9]{0,3}|[1-5][0-9]{4}|[6][0-4][0-9]{3}|[6][5][0-4][0-9]{2}|[6][5][5][0-2][0-9]|[6][5][5][3][0-5]",
"debug": "false|true",
"this_controller": "false|true",
"this_controller_encrypted_only": "false|true",
"netconsole_enabled": "true|false",
"netconsole_host": "",
"netconsole_port": "[1-9][0-9]{0,3}|[1-5][0-9]{4}|[6][0-4][0-9]{3}|[6][5][0-4][0-9]{2}|[6][5][5][0-2][0-9]|[6][5][5][3][0-5]"
"netconsole_port": "[1-9][0-9]{0,3}|[1-5][0-9]{4}|[6][0-4][0-9]{3}|[6][5][0-4][0-9]{2}|[6][5][5][0-2][0-9]|[6][5][5][3][0-5]",
"port": "[1-9][0-9]{0,3}|[1-5][0-9]{4}|[6][0-4][0-9]{3}|[6][5][0-4][0-9]{2}|[6][5][5][0-2][0-9]|[6][5][5][3][0-5]",
"this_controller": "false|true",
"this_controller_encrypted_only": "false|true"
}

View File

@@ -1,7 +1,7 @@
{
"community": ".{1,256}",
"enabled": "true|false",
"enabledV3": "true|false",
"community": ".{1,256}",
"username": "[a-zA-Z0-9_-]{1,30}",
"x_password": "[^'\"]{8,32}"
}

View File

@@ -1,9 +1,9 @@
{
"device_auth": "",
"device_id": "",
"enabled": "true|false",
"ubic_uuid": "",
"x_certificate_arn": "",
"x_certificate_pem": "",
"device_id": "",
"device_auth": "",
"x_private_key": "",
"ubic_uuid": ""
"x_private_key": ""
}

View File

@@ -0,0 +1,4 @@
{
"hostname": "",
"name": ""
}

View File

@@ -1,49 +1,49 @@
{
"override_inform_host": "true|false",
"analytics_disapproved_for": "",
"auto_upgrade": "true|false",
"discoverable": "true|false",
"led_enabled": "true|false",
"live_chat": "disabled|super-only|everyone",
"live_updates": "disabled|live|auto",
"store_enabled": "disabled|super-only|everyone",
"x_ssh_username": "",
"x_ssh_password": "",
"autobackup_enabled": "true|false",
"autobackup_cron_expr": "",
"autobackup_days": "-?[\\d]+",
"autobackup_max_files": "[\\d]+",
"autobackup_timezone": "",
"autobackup_s3_access_key": "",
"autobackup_s3_access_secret": "",
"autobackup_s3_bucket": "",
"autobackup_gcs_certificate_path": "",
"autobackup_enabled": "true|false",
"autobackup_gcs_bucket": "",
"autobackup_gcs_certificate_path": "",
"autobackup_local_path": "",
"autobackup_max_files": "[\\d]+",
"autobackup_post_actions": [
"copy_local|copy_s3|copy_gcs|copy_cloud"
],
"autobackup_s3_access_key": "",
"autobackup_s3_access_secret": "",
"autobackup_s3_bucket": "",
"autobackup_timezone": "",
"backup_to_cloud_enabled": "true|false",
"google_maps_api_key": "",
"minimum_usable_hd_space": "[\\d]+",
"minimum_usable_sd_space": "[\\d]+",
"image_maps_use_google_engine": "true|false",
"data_retention_time_enabled": "true|false",
"data_retention_time_in_hours_for_5minutes_scale": "-?[\\d]+",
"data_retention_time_in_hours_for_hourly_scale": "-?[\\d]+",
"data_retention_time_in_hours_for_daily_scale": "-?[\\d]+",
"data_retention_time_in_hours_for_monthly_scale": "-?[\\d]+",
"data_retention_time_in_hours_for_others": "-?[\\d]+",
"time_series_per_client_stats_enabled": "true|false",
"contact_info_full_name": "",
"contact_info_city": "",
"contact_info_company_name": "",
"contact_info_country": "",
"contact_info_full_name": "",
"contact_info_phone_number": "",
"contact_info_shipping_address_1": "",
"contact_info_shipping_address_2": "",
"contact_info_city": "",
"contact_info_state": "",
"contact_info_zip": "",
"contact_info_country": "",
"data_retention_time_enabled": "true|false",
"data_retention_time_in_hours_for_5minutes_scale": "-?[\\d]+",
"data_retention_time_in_hours_for_daily_scale": "-?[\\d]+",
"data_retention_time_in_hours_for_hourly_scale": "-?[\\d]+",
"data_retention_time_in_hours_for_monthly_scale": "-?[\\d]+",
"data_retention_time_in_hours_for_others": "-?[\\d]+",
"default_site_device_auth_password_alert": "false",
"discoverable": "true|false",
"enable_analytics": "true|false",
"analytics_disapproved_for": ""
"google_maps_api_key": "",
"image_maps_use_google_engine": "true|false",
"led_enabled": "true|false",
"live_chat": "disabled|super-only|everyone",
"live_updates": "disabled|live|auto",
"minimum_usable_hd_space": "[\\d]+",
"minimum_usable_sd_space": "[\\d]+",
"override_inform_host": "true|false",
"store_enabled": "disabled|super-only|everyone",
"time_series_per_client_stats_enabled": "true|false",
"x_ssh_password": "",
"x_ssh_username": ""
}

View File

@@ -1,14 +1,14 @@
{
"enabled": "true|false",
"migrated": "true|false",
"ubic_uuid": "",
"auth_token": "",
"device_id": "",
"enabled": "true|false",
"migrated": "true|false",
"oauth_app_id": "",
"oauth_enabled": "true|false",
"oauth_redirect_uris": [
""
],
"oauth_app_id": "",
"x_oauth_app_secret": "",
"sso_login_enabled": ""
"sso_login_enabled": "",
"ubic_uuid": "",
"x_oauth_app_secret": ""
}

View File

@@ -3,9 +3,9 @@
"host": "",
"port": "[1-9][0-9]{0,3}|[1-5][0-9]{4}|[6][0-4][0-9]{3}|[6][5][0-4][0-9]{2}|[6][5][5][0-2][0-9]|[6][5][5][3][0-5]|^$",
"sender": "",
"use_ssl": "true|false",
"use_sender": "true|false",
"use_auth": "true|false",
"use_sender": "true|false",
"use_ssl": "true|false",
"username": "",
"x_password": ""
}

View File

@@ -1,28 +1,44 @@
{
"arp_cache_base_reachable": "^$|^[1-9]{1}[0-9]{0,4}$",
"arp_cache_timeout": "normal|min-dhcp-lease|custom",
"broadcast_ping": "true|false",
"dhcp_relay_agents_packets": "append|discard|forward|replace|^$",
"dhcp_relay_hop_count": "([1-9]|[1-8][0-9]|9[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])|^$",
"dhcp_relay_max_size": "(6[4-9]|[7-9][0-9]|[1-8][0-9]{2}|9[0-8][0-9]|99[0-9]|1[0-3][0-9]{2}|1400)|^$",
"dhcp_relay_port": "[1-9][0-9]{0,3}|[1-5][0-9]{4}|[6][0-4][0-9]{3}|[6][5][0-4][0-9]{2}|[6][5][5][0-2][0-9]|[6][5][5][3][0-5]|^$",
"dhcp_relay_server_1": "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^$",
"dhcp_relay_server_2": "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^$",
"dhcp_relay_server_3": "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^$",
"dhcp_relay_server_4": "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^$",
"dhcp_relay_server_5": "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^$",
"dhcp_relay_hop_count": "([1-9]|[1-8][0-9]|9[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])|^$",
"dhcp_relay_max_size": "(6[4-9]|[7-9][0-9]|[1-8][0-9]{2}|9[0-8][0-9]|99[0-9]|1[0-3][0-9]{2}|1400)|^$",
"dhcp_relay_port": "[1-9][0-9]{0,3}|[1-5][0-9]{4}|[6][0-4][0-9]{3}|[6][5][0-4][0-9]{2}|[6][5][5][0-2][0-9]|[6][5][5][3][0-5]|^$",
"dhcp_relay_agents_packets": "append|discard|forward|replace|^$",
"upnp_enabled": "true|false",
"upnp_wan_interface": "WAN|WAN2",
"upnp_nat_pmp_enabled": "true|false",
"upnp_secure_mode": "true|false",
"firewall_wan_default_log": "true|false",
"firewall_lan_default_log": "true|false",
"dhcpd_hostfile_update": "true|false",
"dhcpd_use_dnsmasq": "true|false",
"dnsmasq_all_servers": "true|false",
"echo_server": "[^\\\"\\' ]{1,255}",
"firewall_guest_default_log": "true|false",
"firewall_lan_default_log": "true|false",
"firewall_wan_default_log": "true|false",
"ftp_module": "true|false",
"geo_ip_filtering_block": "block|allow",
"geo_ip_filtering_countries": "^([A-Z]{2})?(,[A-Z]{2}){0,149}$",
"geo_ip_filtering_enabled": "true|false",
"geo_ip_filtering_traffic_direction": "^(both|ingress|egress)$",
"gre_module": "true|false",
"h323_module": "true|false",
"pptp_module": "true|false",
"sip_module": "true|false",
"tftp_module": "true|false",
"icmp_timeout": "",
"lldp_enable_all": "true|false",
"mdns_enabled": "true|false",
"mss_clamp": "auto|custom|disabled",
"mss_clamp_mss": "[1-9][0-9]{2,3}",
"offload_accounting": "true|false",
"offload_l2_blocking": "true|false",
"offload_sch": "true|false",
"other_timeout": "",
"pptp_module": "true|false",
"receive_redirects": "true|false",
"send_redirects": "true|false",
"sip_module": "true|false",
"syn_cookies": "true|false",
"tcp_close_timeout": "",
"tcp_close_wait_timeout": "",
"tcp_established_timeout": "",
@@ -31,27 +47,11 @@
"tcp_syn_recv_timeout": "",
"tcp_syn_sent_timeout": "",
"tcp_time_wait_timeout": "",
"tftp_module": "true|false",
"udp_other_timeout": "",
"udp_stream_timeout": "",
"broadcast_ping": "true|false",
"receive_redirects": "true|false",
"send_redirects": "true|false",
"syn_cookies": "true|false",
"offload_accounting": "true|false",
"offload_l2_blocking": "true|false",
"offload_sch": "true|false",
"mdns_enabled": "true|false",
"echo_server": "[^\\\"\\' ]{1,255}",
"lldp_enable_all": "true|false",
"mss_clamp_mss": "[1-9][0-9]{2,3}",
"mss_clamp": "auto|custom|disabled",
"dhcpd_use_dnsmasq": "true|false",
"dhcpd_hostfile_update": "true|false",
"geo_ip_filtering_enabled": "true|false",
"geo_ip_filtering_block": "block|allow",
"geo_ip_filtering_countries": "^([A-Z]{2})?(,[A-Z]{2}){0,149}$",
"geo_ip_filtering_traffic_direction": "^(both|ingress|egress)$",
"arp_cache_timeout": "normal|min-dhcp-lease|custom",
"arp_cache_base_reachable": "^$|^[1-9]{1}[0-9]{0,4}$",
"dnsmasq_all_servers": "true|false"
"upnp_enabled": "true|false",
"upnp_nat_pmp_enabled": "true|false",
"upnp_secure_mode": "true|false",
"upnp_wan_interface": "WAN|WAN2"
}

View File

@@ -1,11 +0,0 @@
#!/usr/bin/env bash
ver="$1"
keys=$(jq -r keys[] "$ver/Setting.json")
while IFS= read -r key; do
readarray -td ' ' arr <<< "${key//_/ }"
fn=$(printf %s "${arr[@]^}")
echo "... $key $fn ..."
jq ".$key" "$ver/Setting.json" > "$ver/Setting$fn.json"
done <<< "$keys"

195
fields/extract.go Normal file
View File

@@ -0,0 +1,195 @@
package main
import (
"archive/tar"
"archive/zip"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"path"
"path/filepath"
"strings"
"github.com/iancoleman/strcase"
"github.com/ulikunitz/xz"
"github.com/xor-gate/ar"
)
func downloadJar(version string) (string, error) {
url := fmt.Sprintf("https://dl.ui.com/unifi/%s/unifi_sysvinit_all.deb", version)
// debFile, err := ioutil.TempFile("", "go-unifi-fields*.deb")
// if err != nil {
// return err
// }
// defer debFile.Close()
// fmt.Println(debFile.Name())
// defer os.Remove(debFile.Name())
debResp, err := http.Get(url)
if err != nil {
return "", fmt.Errorf("unable to download deb: %w", err)
}
defer debResp.Body.Close()
// _, err = io.Copy(debFile, debResp.Body)
// if err != nil {
// return err
// }
var uncompressedReader io.Reader
arReader := ar.NewReader(debResp.Body)
for true {
header, err := arReader.Next()
if err == io.EOF || header == nil {
break
}
if err != nil {
return "", fmt.Errorf("in ar next: %w", err)
}
// read the data file
if header.Name == "data.tar.xz" {
uncompressedReader, err = xz.NewReader(arReader)
if err != nil {
return "", fmt.Errorf("in xz reader: %w", err)
}
break
}
}
if uncompressedReader == nil {
return "", fmt.Errorf("unable to find .deb data file")
}
tarReader := tar.NewReader(uncompressedReader)
var aceJar *os.File
for true {
header, err := tarReader.Next()
if err == io.EOF {
break
}
if err != nil {
return "", fmt.Errorf("in next: %w", err)
}
if header.Typeflag != tar.TypeReg || header.Name != "./usr/lib/unifi/lib/ace.jar" {
// skipping
continue
}
aceJar, err = ioutil.TempFile("", fmt.Sprintf("ace-%s-*.jar", version))
if err != nil {
return "", fmt.Errorf("unable to create temp file: %w", err)
}
_, err = io.Copy(aceJar, tarReader)
if err != nil {
return "", fmt.Errorf("unable to write ace.jar temp file: %w", err)
}
}
if aceJar == nil {
return "", fmt.Errorf("unable to find ace.jar")
}
defer aceJar.Close()
return aceJar.Name(), nil
}
func extractJSON(jarFile, fieldsDir string) error {
jarZip, err := zip.OpenReader(jarFile)
if err != nil {
return fmt.Errorf("unable to open jar: %w", err)
}
defer jarZip.Close()
err = os.MkdirAll(fieldsDir, 0755)
if err != nil {
return fmt.Errorf("unable to create fields dir: %w", err)
}
for _, f := range jarZip.File {
if !strings.HasPrefix(f.Name, "api/fields/") || path.Ext(f.Name) != ".json" {
// skip file
continue
}
err = func() error {
src, err := f.Open()
if err != nil {
return err
}
dst, err := os.Create(filepath.Join(fieldsDir, filepath.Base(f.Name)))
if err != nil {
return err
}
defer dst.Close()
_, err = io.Copy(dst, src)
if err != nil {
return err
}
return nil
}()
if err != nil {
return fmt.Errorf("unable to write JSON file: %w", err)
}
}
settingsData, err := ioutil.ReadFile(filepath.Join(fieldsDir, "Setting.json"))
if errors.Is(err, os.ErrNotExist) {
return nil
}
if err != nil {
return fmt.Errorf("unable to open settings file: %w", err)
}
var settings map[string]interface{}
err = json.Unmarshal(settingsData, &settings)
if err != nil {
return fmt.Errorf("unable to unmarshal settings: %w", err)
}
for k, v := range settings {
fileName := fmt.Sprintf("Setting%s.json", strcase.ToCamel(k))
data, err := json.MarshalIndent(v, "", " ")
if err != nil {
return fmt.Errorf("unable to marshal setting %q: %w", k, err)
}
err = ioutil.WriteFile(filepath.Join(fieldsDir, fileName), data, 0755)
if err != nil {
return fmt.Errorf("unable to write new settings file: %w", err)
}
}
/*
#!/usr/bin/env bash
ver="$1"
keys=$(jq -r keys[] "$ver/Setting.json")
while IFS= read -r key; do
readarray -td ' ' arr <<< "${key//_/ }"
fn=$(printf %s "${arr[@]^}")
echo "... $key $fn ..."
jq ".$key" "$ver/Setting.json" > "$ver/Setting$fn.json"
done <<< "$keys"
*/
// TODO: cleanup JSON
return nil
}

View File

@@ -1,25 +0,0 @@
#!/usr/bin/env bash
deburl="https://dl.ui.com/unifi/$1/unifi_sysvinit_all.deb"
wkdir="$(mktemp -d)"
deb="$wkdir\unifi.deb"
curl -o "$deb" "$deburl"
mkdir -p "$wkdir/unifi"
dpkg-deb -R "$deb" "$wkdir/unifi"
cp "$wkdir/unifi/usr/lib/unifi/lib/ace.jar" ./
unzip -o ace.jar -d ./ace/
mkdir -p "$1"
cp ./ace/api/fields/*.json "./$1/"
./cleanupjson.sh "$1"
rm -rf ace ace.jar
go run main.go "$1" "../unifi/"
gofmt -w -s ./../unifi/

View File

@@ -2,7 +2,9 @@ package main
import (
"bytes"
_ "embed"
"encoding/json"
"errors"
"flag"
"fmt"
"io"
@@ -171,7 +173,7 @@ func cleanName(name string, reps []replacement) string {
}
func usage() {
fmt.Printf("Usage: %s [OPTIONS] versionDir outputDir\n", path.Base(os.Args[0]))
fmt.Printf("Usage: %s [OPTIONS] version\n", path.Base(os.Args[0]))
flag.PrintDefaults()
}
@@ -179,32 +181,61 @@ func main() {
flag.Usage = usage
noEmbeddedTypes := flag.Bool("no-embedded-types", true, "Whether to generate top-level type definitions for embedded type definitions")
noEmbeddedTypesFlag := flag.Bool("no-embedded-types", true, "Whether to generate top-level type definitions for embedded type definitions")
versionBaseDirFlag := flag.String("version-base-dir", ".", "The base directory for version JSON files")
outputDirFlag := flag.String("output-dir", ".", "The output directory of the generated Go code")
downloadOnly := flag.Bool("download-only", false, "Only download and build the fields JSON directory, do not generate")
flag.Parse()
versionDir := flag.Arg(0)
outputDir := flag.Arg(1)
embedTypes = !*noEmbeddedTypes
embedTypes = !*noEmbeddedTypesFlag
versionDir := flag.Arg(0)
if versionDir == "" {
fmt.Print("error: no version directory specified\n\n")
usage()
os.Exit(1)
}
if outputDir == "" {
fmt.Print("error: no output directory specified\n\n")
usage()
os.Exit(1)
}
wd, err := os.Getwd()
if err != nil {
panic(err)
}
fieldsDir := filepath.Join(wd, versionDir)
outDir := filepath.Join(wd, outputDir)
fieldsDir := filepath.Join(wd, *versionBaseDirFlag, versionDir)
outDir := filepath.Join(wd, *outputDirFlag)
fieldsInfo, err := os.Stat(fieldsDir)
if err != nil {
if !errors.Is(err, os.ErrNotExist) {
panic(err)
}
// download fields, create
jarFile, err := downloadJar(versionDir)
if err != nil {
panic(err)
}
err = extractJSON(jarFile, fieldsDir)
os.Remove(jarFile)
if err != nil {
panic(err)
}
fieldsInfo, err = os.Stat(fieldsDir)
if err != nil {
panic(err)
}
}
if !fieldsInfo.IsDir() {
panic("version info isn't a directory")
}
if *downloadOnly {
fmt.Println("Fields JSON ready!")
os.Exit(0)
}
fieldsFiles, err := ioutil.ReadDir(fieldsDir)
if err != nil {
@@ -412,6 +443,9 @@ func (r *Resource) processJSON(b []byte) error {
return nil
}
//go:embed api.go.tmpl
var apiGoTemplate string
func (r *Resource) generateCode() (string, error) {
var err error
var buf bytes.Buffer
@@ -419,7 +453,7 @@ func (r *Resource) generateCode() (string, error) {
tpl := template.Must(template.New("api.go.tmpl").Funcs(template.FuncMap{
"embedTypes": func() bool { return embedTypes },
}).ParseFiles("api.go.tmpl"))
}).Parse(apiGoTemplate))
tpl.Execute(writer, r)

5
go.mod
View File

@@ -1,11 +1,14 @@
module github.com/paultyng/go-unifi
go 1.15
// set to use embed for generation
go 1.16
require (
github.com/iancoleman/strcase v0.1.3
github.com/kr/pretty v0.1.0 // indirect
github.com/stretchr/testify v1.7.0
github.com/tj/assert v0.0.3
github.com/ulikunitz/xz v0.5.10
github.com/xor-gate/ar v0.0.0-20170530204233-5c72ae81e2b7
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
)

8
go.sum
View File

@@ -1,4 +1,3 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -12,17 +11,18 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tj/assert v0.0.3 h1:Df/BlaZ20mq6kuai7f5z2TvPFiwC3xaWJSDQNiIS3Rk=
github.com/tj/assert v0.0.3/go.mod h1:Ne6X72Q+TB1AteidzQncjw9PabbMp4PBMZ1k+vd1Pvk=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8=
github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/xor-gate/ar v0.0.0-20170530204233-5c72ae81e2b7 h1:Vo3q7h44BfmnLQh5SdF+2xwIoVnHThmZLunx6odjrHI=
github.com/xor-gate/ar v0.0.0-20170530204233-5c72ae81e2b7/go.mod h1:TCWCUPhQU1j7axqROa/VHnlgJGHthAOqJZahg7b/DUc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c h1:grhR+C34yXImVGp7EzNk+DTIk+323eIUWOmEevy6bDo=
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -1,126 +0,0 @@
// Code generated from ace.jar fields *.json files
// DO NOT EDIT.
package unifi
import (
"context"
"encoding/json"
"fmt"
)
// just to fix compile issues with the import
var (
_ context.Context
_ fmt.Formatter
_ json.Marshaler
)
type APGroup struct {
ID string `json:"_id,omitempty"`
SiteID string `json:"site_id,omitempty"`
Hidden bool `json:"attr_hidden,omitempty"`
HiddenID string `json:"attr_hidden_id,omitempty"`
NoDelete bool `json:"attr_no_delete,omitempty"`
NoEdit bool `json:"attr_no_edit,omitempty"`
Name string `json:"name,omitempty"` // .{1,128}
}
func (dst *APGroup) UnmarshalJSON(b []byte) error {
type Alias APGroup
aux := &struct {
*Alias
}{
Alias: (*Alias)(dst),
}
err := json.Unmarshal(b, &aux)
if err != nil {
return fmt.Errorf("unable to unmarshal alias: %w", err)
}
return nil
}
func (c *Client) listAPGroup(ctx context.Context, site string) ([]APGroup, error) {
var respBody struct {
Meta meta `json:"meta"`
Data []APGroup `json:"data"`
}
err := c.do(ctx, "GET", fmt.Sprintf("s/%s/apgroups", site), nil, &respBody)
if err != nil {
return nil, err
}
return respBody.Data, nil
}
func (c *Client) getAPGroup(ctx context.Context, site, id string) (*APGroup, error) {
var respBody struct {
Meta meta `json:"meta"`
Data []APGroup `json:"data"`
}
err := c.do(ctx, "GET", fmt.Sprintf("s/%s/rest/apgroups/%s", site, id), nil, &respBody)
if err != nil {
return nil, err
}
if len(respBody.Data) != 1 {
return nil, &NotFoundError{}
}
d := respBody.Data[0]
return &d, nil
}
func (c *Client) deleteAPGroup(ctx context.Context, site, id string) error {
err := c.do(ctx, "DELETE", fmt.Sprintf("s/%s/rest/apgroups/%s", site, id), struct{}{}, nil)
if err != nil {
return err
}
return nil
}
func (c *Client) createAPGroup(ctx context.Context, site string, d *APGroup) (*APGroup, error) {
var respBody struct {
Meta meta `json:"meta"`
Data []APGroup `json:"data"`
}
err := c.do(ctx, "POST", fmt.Sprintf("s/%s/rest/apgroups", site), d, &respBody)
if err != nil {
return nil, err
}
if len(respBody.Data) != 1 {
return nil, &NotFoundError{}
}
new := respBody.Data[0]
return &new, nil
}
func (c *Client) updateAPGroup(ctx context.Context, site string, d *APGroup) (*APGroup, error) {
var respBody struct {
Meta meta `json:"meta"`
Data []APGroup `json:"data"`
}
err := c.do(ctx, "PUT", fmt.Sprintf("s/%s/rest/apgroups/%s", site, d.ID), d, &respBody)
if err != nil {
return nil, err
}
if len(respBody.Data) != 1 {
return nil, &NotFoundError{}
}
new := respBody.Data[0]
return &new, nil
}

View File

@@ -281,7 +281,7 @@ type DeviceRadioTable struct {
Ht string `json:"ht,omitempty"` // 20|40|80|160|1080|2160
LoadbalanceEnabled bool `json:"loadbalance_enabled,omitempty"`
Maxsta int `json:"maxsta,omitempty"` // [1-9]|[1-9][0-9]|1[0-9]{2}|200|^$
MinRssi int `json:"min_rssi,omitempty"` // ^-([1-9]|[1-8][0-9]|9[0-4])$
MinRssi int `json:"min_rssi,omitempty"` // ^-(6[7-9]|[7-8][0-9]|90)$
MinRssiEnabled bool `json:"min_rssi_enabled,omitempty"`
Name string `json:"name,omitempty"`
Radio string `json:"radio,omitempty"` // ng|na|ad

6
unifi/gen.go Normal file
View File

@@ -0,0 +1,6 @@
package unifi
// This will generate the *.generated.go files in this package for the specified
// Unifi controller version.
//go:generate go run ../fields/ -version-base-dir=../fields/ 6.0.43
//go:generate gofmt -w -s ./