commit dd77b199e0b7ef90b81d0f238bb400a41f44b58b Author: DJP Date: Tue Sep 9 15:26:40 2025 -0400 Initial commit: Meta File Server Query Application - Flask web application for querying Oliver metafile server - Replicates Make.com workflow for job data retrieval - Two-stage process: XML client extraction → JSON data retrieval - Clean HTML interface with responsive design - Comprehensive error handling and SSL fixes - Complete documentation and installation guides 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46ff21f --- /dev/null +++ b/.gitignore @@ -0,0 +1,62 @@ +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# Virtual Environment +meta-server-venv/ +venv/ +env/ +ENV/ + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +# Logs +*.log + +# Flask +instance/ +.webassets-cache + +# Environment variables +.env +.env.local +.env.production + +# Temporary files +*.tmp +*.temp \ No newline at end of file diff --git a/AI COMPANION Metafile Server Grabber.blueprint.json b/AI COMPANION Metafile Server Grabber.blueprint.json new file mode 100644 index 0000000..a7dfff7 --- /dev/null +++ b/AI COMPANION Metafile Server Grabber.blueprint.json @@ -0,0 +1,1538 @@ +{ + "name": "AI COMPANION Metafile Server Grabber", + "flow": [ + { + "id": 3, + "module": "gateway:CustomWebHook", + "version": 1, + "parameters": { + "hook": 20218, + "maxResults": 1 + }, + "mapper": {}, + "metadata": { + "designer": { + "x": -989, + "y": -285 + }, + "restore": { + "parameters": { + "hook": { + "data": { + "editable": "true" + }, + "label": "AI COMPANION METAFILE HOOK" + } + } + }, + "parameters": [ + { + "name": "hook", + "type": "hook:gateway-webhook", + "label": "Webhook", + "required": true + }, + { + "name": "maxResults", + "type": "number", + "label": "Maximum number of results" + } + ], + "interface": [ + { + "name": "client", + "type": "text" + }, + { + "name": "deliverableNumber", + "type": "text" + } + ] + } + }, + { + "id": 15, + "module": "builtin:BasicRouter", + "version": 1, + "mapper": null, + "metadata": { + "designer": { + "x": -646, + "y": -299 + } + }, + "routes": [ + { + "flow": [ + { + "id": 17, + "module": "util:SetVariables", + "version": 1, + "parameters": {}, + "mapper": { + "scope": "roundtrip", + "variables": [ + { + "name": "deliverable", + "value": "{{3.deliverableNumber}}" + }, + { + "name": "file", + "value": "{{3.deliverableNumber}}.xml" + }, + { + "name": "keyid", + "value": "1" + }, + { + "name": "root", + "value": "XML" + }, + { + "name": "metafile_key", + "value": "$14W0TF~8FL" + } + ] + }, + "metadata": { + "designer": { + "x": -41, + "y": -849 + }, + "restore": { + "expect": { + "scope": { + "label": "One cycle" + }, + "variables": { + "items": [ + null, + null, + null, + null, + null + ] + } + } + }, + "expect": [ + { + "name": "variables", + "spec": [ + { + "name": "name", + "type": "text", + "label": "Variable name", + "required": true + }, + { + "name": "value", + "type": "any", + "label": "Variable value" + } + ], + "type": "array", + "label": "Variables" + }, + { + "name": "scope", + "type": "select", + "label": "Variable lifetime", + "required": true, + "validate": { + "enum": [ + "roundtrip", + "execution" + ] + } + } + ], + "interface": [ + { + "name": "deliverable", + "type": "any", + "label": "deliverable" + }, + { + "name": "file", + "type": "any", + "label": "file" + }, + { + "name": "keyid", + "type": "any", + "label": "keyid" + }, + { + "name": "root", + "type": "any", + "label": "root" + }, + { + "name": "metafile_key", + "type": "any", + "label": "metafile_key" + } + ] + } + }, + { + "id": 18, + "module": "util:SetVariables", + "version": 1, + "parameters": {}, + "mapper": { + "scope": "roundtrip", + "variables": [ + { + "name": "checksum", + "value": "{{md5(17.metafile_key + 17.root + \"/\" + 17.file)}}" + } + ] + }, + "metadata": { + "designer": { + "x": 265, + "y": -844, + "name": "Checksum" + }, + "restore": { + "expect": { + "scope": { + "label": "One cycle" + }, + "variables": { + "items": [ + null + ] + } + } + }, + "expect": [ + { + "name": "variables", + "spec": [ + { + "name": "name", + "type": "text", + "label": "Variable name", + "required": true + }, + { + "name": "value", + "type": "any", + "label": "Variable value" + } + ], + "type": "array", + "label": "Variables" + }, + { + "name": "scope", + "type": "select", + "label": "Variable lifetime", + "required": true, + "validate": { + "enum": [ + "roundtrip", + "execution" + ] + } + } + ], + "interface": [ + { + "name": "checksum", + "type": "any", + "label": "checksum" + } + ] + } + }, + { + "id": 19, + "module": "http:ActionSendData", + "version": 3, + "parameters": { + "handleErrors": true, + "useNewZLibDeCompress": true + }, + "mapper": { + "ca": "", + "qs": [], + "url": "https://metafile.oliver.solutions/getFile?root={{17.root}}&path={{17.file}}&keyid={{17.keyid}}&cs={{18.checksum}}", + "data": "", + "gzip": true, + "method": "get", + "headers": [], + "timeout": "", + "useMtls": false, + "authPass": "", + "authUser": "", + "bodyType": "raw", + "contentType": "text/xml", + "serializeUrl": false, + "shareCookies": false, + "parseResponse": false, + "followRedirect": false, + "useQuerystring": false, + "rejectUnauthorized": false + }, + "metadata": { + "designer": { + "x": 567, + "y": -843 + }, + "restore": { + "expect": { + "qs": { + "mode": "chose" + }, + "method": { + "mode": "chose", + "label": "GET" + }, + "headers": { + "mode": "chose" + }, + "bodyType": { + "label": "Raw" + }, + "contentType": { + "label": "XML (text/xml)" + } + } + }, + "parameters": [ + { + "name": "handleErrors", + "type": "boolean", + "label": "Evaluate all states as errors (except for 2xx and 3xx )", + "required": true + }, + { + "name": "useNewZLibDeCompress", + "type": "hidden" + } + ], + "expect": [ + { + "name": "url", + "type": "url", + "label": "URL", + "required": true + }, + { + "name": "serializeUrl", + "type": "boolean", + "label": "Serialize URL", + "required": true + }, + { + "name": "method", + "type": "select", + "label": "Method", + "required": true, + "validate": { + "enum": [ + "get", + "head", + "post", + "put", + "patch", + "delete", + "options" + ] + } + }, + { + "name": "headers", + "spec": [ + { + "name": "name", + "type": "text", + "label": "Name", + "required": true + }, + { + "name": "value", + "type": "text", + "label": "Value" + } + ], + "type": "array", + "label": "Headers" + }, + { + "name": "qs", + "spec": [ + { + "name": "name", + "type": "text", + "label": "Name", + "required": true + }, + { + "name": "value", + "type": "text", + "label": "Value" + } + ], + "type": "array", + "label": "Query String" + }, + { + "name": "bodyType", + "type": "select", + "label": "Body type", + "validate": { + "enum": [ + "raw", + "x_www_form_urlencoded", + "multipart_form_data" + ] + } + }, + { + "name": "parseResponse", + "type": "boolean", + "label": "Parse response", + "required": true + }, + { + "name": "authUser", + "type": "text", + "label": "User name" + }, + { + "name": "authPass", + "type": "password", + "label": "Password" + }, + { + "name": "timeout", + "type": "uinteger", + "label": "Timeout", + "validate": { + "max": 300, + "min": 1 + } + }, + { + "name": "shareCookies", + "type": "boolean", + "label": "Share cookies with other HTTP modules", + "required": true + }, + { + "name": "ca", + "type": "cert", + "label": "Self-signed certificate" + }, + { + "name": "rejectUnauthorized", + "type": "boolean", + "label": "Reject connections that are using unverified (self-signed) certificates", + "required": true + }, + { + "name": "followRedirect", + "type": "boolean", + "label": "Follow redirect", + "required": true + }, + { + "name": "useQuerystring", + "type": "boolean", + "label": "Disable serialization of multiple same query string keys as arrays", + "required": true + }, + { + "name": "gzip", + "type": "boolean", + "label": "Request compressed content", + "required": true + }, + { + "name": "useMtls", + "type": "boolean", + "label": "Use Mutual TLS", + "required": true + }, + { + "name": "contentType", + "type": "select", + "label": "Content type", + "validate": { + "enum": [ + "text/plain", + "application/json", + "application/xml", + "text/xml", + "text/html", + "custom" + ] + } + }, + { + "name": "data", + "type": "buffer", + "label": "Request content" + } + ], + "advanced": true + } + }, + { + "id": 20, + "module": "util:TransformEncoding", + "version": 1, + "parameters": {}, + "mapper": { + "cpin": "utf8", + "data": "{{19.data}}", + "cpout": "utf8" + }, + "metadata": { + "designer": { + "x": 917, + "y": -843 + }, + "restore": { + "expect": { + "cpin": { + "mode": "chose", + "label": "UTF 8" + }, + "cpout": { + "mode": "chose", + "label": "UTF 8" + } + } + }, + "expect": [ + { + "name": "data", + "type": "buffer", + "label": "Input data" + }, + { + "name": "cpin", + "type": "select", + "label": "Input data codepage", + "validate": { + "enum": [ + "utf8", + "ucs2", + "utf16be", + "cp1250", + "cp1251", + "cp1252", + "cp1253", + "cp1254", + "cp1255", + "cp1256", + "cp1257", + "cp1258", + "cp28591", + "cp28592", + "cp28593", + "cp28594", + "cp28595", + "cp28596", + "cp28597", + "cp28598", + "cp28599", + "cp28600", + "cp28601", + "cp28602", + "cp28603", + "cp28604", + "cp28605", + "cp28606", + "big5", + "big5-hkscs", + "euc-cn", + "euc-jp", + "euc-kr", + "gb18030", + "gb2312", + "gbk", + "ks_c_5601", + "shift_jis", + "windows-31j", + "windows932", + "windows936", + "windows949", + "windows950", + "ascii" + ] + } + }, + { + "name": "cpout", + "type": "select", + "label": "Output data codepage", + "required": true, + "validate": { + "enum": [ + "utf8", + "ucs2", + "utf16be", + "cp1250", + "cp1251", + "cp1252", + "cp1253", + "cp1254", + "cp1255", + "cp1256", + "cp1257", + "cp1258", + "cp28591", + "cp28592", + "cp28593", + "cp28594", + "cp28595", + "cp28596", + "cp28597", + "cp28598", + "cp28599", + "cp28600", + "cp28601", + "cp28602", + "cp28603", + "cp28604", + "cp28605", + "cp28606", + "big5", + "big5-hkscs", + "euc-cn", + "euc-jp", + "euc-kr", + "gb18030", + "gb2312", + "gbk", + "ks_c_5601", + "shift_jis", + "windows-31j", + "windows932", + "windows936", + "windows949", + "windows950", + "ascii" + ] + } + } + ] + } + }, + { + "id": 21, + "module": "xml:ParseXML", + "version": 1, + "parameters": { + "type": 6496, + "preserveNumbersAsText": false + }, + "mapper": { + "xml": "{{20.data}}" + }, + "metadata": { + "designer": { + "x": 1260, + "y": -841 + }, + "restore": { + "parameters": { + "type": { + "label": "XML_CLIENT" + } + } + }, + "parameters": [ + { + "name": "type", + "type": "udt", + "label": "Data structure" + }, + { + "name": "preserveNumbersAsText", + "type": "boolean", + "label": "Preserve numbers as text", + "required": true + } + ], + "expect": [ + { + "name": "xml", + "type": "text", + "label": "XML", + "required": true + } + ], + "interface": [ + { + "name": "client", + "type": "text", + "label": "Client", + "default": null, + "required": true, + "multiline": true + } + ] + } + }, + { + "id": 22, + "module": "util:SetVariable2", + "version": 1, + "parameters": {}, + "mapper": { + "name": "client", + "scope": "roundtrip", + "value": "{{split(21.JobSpecification.Document[].`Set-Property`[].`_attributes`.Value; \">\")}}" + }, + "metadata": { + "designer": { + "x": 1547, + "y": -844 + }, + "restore": { + "expect": { + "scope": { + "label": "One cycle" + } + } + }, + "expect": [ + { + "name": "name", + "type": "text", + "label": "Variable name", + "required": true + }, + { + "name": "scope", + "type": "select", + "label": "Variable lifetime", + "required": true, + "validate": { + "enum": [ + "roundtrip", + "execution" + ] + } + }, + { + "name": "value", + "type": "any", + "label": "Variable value" + } + ], + "interface": [ + { + "name": "client", + "type": "any", + "label": "client" + } + ] + } + }, + { + "id": 23, + "module": "regexp:AdvancedParser", + "version": 1, + "parameters": { + "global": false, + "multiline": false, + "sensitive": true, + "singleline": false, + "advancedParser": true, + "continueWhenNoRes": false + }, + "mapper": { + "text": "{{22.client}}", + "pattern": "[A-Z|_]{0,100}(?=\\/CAMPAIGNS)" + }, + "metadata": { + "designer": { + "x": 1849, + "y": -844 + }, + "restore": {}, + "parameters": [ + { + "name": "global", + "type": "boolean", + "label": "Global match", + "required": true + }, + { + "name": "sensitive", + "type": "boolean", + "label": "Case sensitive", + "required": true + }, + { + "name": "multiline", + "type": "boolean", + "label": "Multiline", + "required": true + }, + { + "name": "singleline", + "type": "boolean", + "label": "Singleline", + "required": true + }, + { + "name": "continueWhenNoRes", + "type": "boolean", + "label": "Continue the execution of the route even if the module finds no matches", + "required": true + }, + { + "name": "advancedParser", + "type": "hidden" + } + ], + "expect": [ + { + "name": "pattern", + "type": "text", + "label": "Pattern", + "required": true + }, + { + "name": "text", + "type": "text", + "label": "Text", + "required": true + } + ] + } + }, + { + "id": 24, + "module": "util:SetVariable2", + "version": 1, + "parameters": {}, + "mapper": { + "name": "client", + "scope": "roundtrip", + "value": "{{23.`__IMTMATCH__`}}" + }, + "metadata": { + "designer": { + "x": 2210, + "y": -842 + }, + "restore": { + "expect": { + "scope": { + "label": "One cycle" + } + } + }, + "expect": [ + { + "name": "name", + "type": "text", + "label": "Variable name", + "required": true + }, + { + "name": "scope", + "type": "select", + "label": "Variable lifetime", + "required": true, + "validate": { + "enum": [ + "roundtrip", + "execution" + ] + } + }, + { + "name": "value", + "type": "any", + "label": "Variable value" + } + ], + "interface": [ + { + "name": "client", + "type": "any", + "label": "client" + } + ] + } + }, + { + "id": 6, + "module": "util:SetVariables", + "version": 1, + "parameters": {}, + "mapper": { + "scope": "roundtrip", + "variables": [ + { + "name": "client", + "value": "{{24.client}}" + }, + { + "name": "file", + "value": "{{3.deliverableNumber}}.json" + }, + { + "name": "keyid", + "value": "1" + }, + { + "name": "root", + "value": "JSON_STORE" + }, + { + "name": "metafile_key", + "value": "$14W0TF~8FL" + } + ] + }, + "metadata": { + "designer": { + "x": -364, + "y": -280 + }, + "restore": { + "expect": { + "scope": { + "label": "One cycle" + }, + "variables": { + "items": [ + null, + null, + null, + null, + null + ] + } + } + }, + "expect": [ + { + "name": "variables", + "spec": [ + { + "name": "name", + "type": "text", + "label": "Variable name", + "required": true + }, + { + "name": "value", + "type": "any", + "label": "Variable value" + } + ], + "type": "array", + "label": "Variables" + }, + { + "name": "scope", + "type": "select", + "label": "Variable lifetime", + "required": true, + "validate": { + "enum": [ + "roundtrip", + "execution" + ] + } + } + ], + "interface": [ + { + "name": "client", + "type": "any", + "label": "client" + }, + { + "name": "file", + "type": "any", + "label": "file" + }, + { + "name": "keyid", + "type": "any", + "label": "keyid" + }, + { + "name": "root", + "type": "any", + "label": "root" + }, + { + "name": "metafile_key", + "type": "any", + "label": "metafile_key" + } + ] + } + }, + { + "id": 7, + "module": "util:SetVariable2", + "version": 1, + "parameters": {}, + "mapper": { + "name": "checksum", + "scope": "roundtrip", + "value": "{{md5(6.metafile_key + 6.root + \"/\" + 6.client + \"/\" + 6.file)}}" + }, + "metadata": { + "designer": { + "x": -64, + "y": -289 + }, + "restore": { + "expect": { + "scope": { + "label": "One cycle" + } + } + }, + "expect": [ + { + "name": "name", + "type": "text", + "label": "Variable name", + "required": true + }, + { + "name": "scope", + "type": "select", + "label": "Variable lifetime", + "required": true, + "validate": { + "enum": [ + "roundtrip", + "execution" + ] + } + }, + { + "name": "value", + "type": "any", + "label": "Variable value" + } + ], + "interface": [ + { + "name": "checksum", + "type": "any", + "label": "checksum" + } + ] + } + }, + { + "id": 8, + "module": "http:ActionSendData", + "version": 3, + "parameters": { + "handleErrors": false, + "useNewZLibDeCompress": true + }, + "mapper": { + "ca": "", + "qs": [], + "url": "https://metafile.oliver.solutions/getFile?root={{6.root}}&sub={{6.client}}&path={{6.file}}&keyid={{6.keyid}}&cs={{7.checksum}}", + "gzip": true, + "method": "get", + "headers": [], + "timeout": "", + "useMtls": false, + "authPass": "", + "authUser": "", + "bodyType": "", + "serializeUrl": false, + "shareCookies": false, + "parseResponse": true, + "followRedirect": false, + "useQuerystring": false, + "rejectUnauthorized": false + }, + "metadata": { + "designer": { + "x": 252, + "y": -286, + "name": "Download JSON From Metaserver" + }, + "restore": { + "expect": { + "qs": { + "mode": "chose" + }, + "method": { + "mode": "chose", + "label": "GET" + }, + "headers": { + "mode": "chose" + }, + "bodyType": { + "label": "Empty" + } + } + }, + "parameters": [ + { + "name": "handleErrors", + "type": "boolean", + "label": "Evaluate all states as errors (except for 2xx and 3xx )", + "required": true + }, + { + "name": "useNewZLibDeCompress", + "type": "hidden" + } + ], + "expect": [ + { + "name": "url", + "type": "url", + "label": "URL", + "required": true + }, + { + "name": "serializeUrl", + "type": "boolean", + "label": "Serialize URL", + "required": true + }, + { + "name": "method", + "type": "select", + "label": "Method", + "required": true, + "validate": { + "enum": [ + "get", + "head", + "post", + "put", + "patch", + "delete", + "options" + ] + } + }, + { + "name": "headers", + "spec": [ + { + "name": "name", + "type": "text", + "label": "Name", + "required": true + }, + { + "name": "value", + "type": "text", + "label": "Value" + } + ], + "type": "array", + "label": "Headers" + }, + { + "name": "qs", + "spec": [ + { + "name": "name", + "type": "text", + "label": "Name", + "required": true + }, + { + "name": "value", + "type": "text", + "label": "Value" + } + ], + "type": "array", + "label": "Query String" + }, + { + "name": "bodyType", + "type": "select", + "label": "Body type", + "validate": { + "enum": [ + "raw", + "x_www_form_urlencoded", + "multipart_form_data" + ] + } + }, + { + "name": "parseResponse", + "type": "boolean", + "label": "Parse response", + "required": true + }, + { + "name": "authUser", + "type": "text", + "label": "User name" + }, + { + "name": "authPass", + "type": "password", + "label": "Password" + }, + { + "name": "timeout", + "type": "uinteger", + "label": "Timeout", + "validate": { + "max": 300, + "min": 1 + } + }, + { + "name": "shareCookies", + "type": "boolean", + "label": "Share cookies with other HTTP modules", + "required": true + }, + { + "name": "ca", + "type": "cert", + "label": "Self-signed certificate" + }, + { + "name": "rejectUnauthorized", + "type": "boolean", + "label": "Reject connections that are using unverified (self-signed) certificates", + "required": true + }, + { + "name": "followRedirect", + "type": "boolean", + "label": "Follow redirect", + "required": true + }, + { + "name": "useQuerystring", + "type": "boolean", + "label": "Disable serialization of multiple same query string keys as arrays", + "required": true + }, + { + "name": "gzip", + "type": "boolean", + "label": "Request compressed content", + "required": true + }, + { + "name": "useMtls", + "type": "boolean", + "label": "Use Mutual TLS", + "required": true + } + ], + "advanced": true + } + }, + { + "id": 4, + "module": "util:TransformEncoding", + "version": 1, + "parameters": {}, + "filter": { + "name": "", + "conditions": [ + [ + { + "a": "{{8.statusCode}}", + "b": "200", + "o": "text:equal" + } + ] + ] + }, + "mapper": { + "cpin": "utf8", + "data": "{{8.data}}", + "cpout": "utf8" + }, + "metadata": { + "designer": { + "x": 589, + "y": -281 + }, + "restore": { + "expect": { + "cpin": { + "mode": "chose", + "label": "UTF 8" + }, + "cpout": { + "mode": "chose", + "label": "UTF 8" + } + } + }, + "expect": [ + { + "name": "data", + "type": "buffer", + "label": "Input data" + }, + { + "name": "cpin", + "type": "select", + "label": "Input data codepage", + "validate": { + "enum": [ + "utf8", + "ucs2", + "utf16be", + "cp1250", + "cp1251", + "cp1252", + "cp1253", + "cp1254", + "cp1255", + "cp1256", + "cp1257", + "cp1258", + "cp28591", + "cp28592", + "cp28593", + "cp28594", + "cp28595", + "cp28596", + "cp28597", + "cp28598", + "cp28599", + "cp28600", + "cp28601", + "cp28602", + "cp28603", + "cp28604", + "cp28605", + "cp28606", + "big5", + "big5-hkscs", + "euc-cn", + "euc-jp", + "euc-kr", + "gb18030", + "gb2312", + "gbk", + "ks_c_5601", + "shift_jis", + "windows-31j", + "windows932", + "windows936", + "windows949", + "windows950", + "ascii" + ] + } + }, + { + "name": "cpout", + "type": "select", + "label": "Output data codepage", + "required": true, + "validate": { + "enum": [ + "utf8", + "ucs2", + "utf16be", + "cp1250", + "cp1251", + "cp1252", + "cp1253", + "cp1254", + "cp1255", + "cp1256", + "cp1257", + "cp1258", + "cp28591", + "cp28592", + "cp28593", + "cp28594", + "cp28595", + "cp28596", + "cp28597", + "cp28598", + "cp28599", + "cp28600", + "cp28601", + "cp28602", + "cp28603", + "cp28604", + "cp28605", + "cp28606", + "big5", + "big5-hkscs", + "euc-cn", + "euc-jp", + "euc-kr", + "gb18030", + "gb2312", + "gbk", + "ks_c_5601", + "shift_jis", + "windows-31j", + "windows932", + "windows936", + "windows949", + "windows950", + "ascii" + ] + } + } + ] + } + }, + { + "id": 5, + "module": "json:ParseJSON", + "version": 1, + "parameters": { + "type": "" + }, + "mapper": { + "json": "{{4.data}}" + }, + "metadata": { + "designer": { + "x": 931, + "y": -289 + }, + "restore": { + "parameters": { + "type": { + "label": "Choose a data structure" + } + } + }, + "parameters": [ + { + "name": "type", + "type": "udt", + "label": "Data structure" + } + ], + "expect": [ + { + "name": "json", + "type": "text", + "label": "JSON string", + "required": true + } + ] + } + }, + { + "id": 9, + "module": "gateway:WebhookRespond", + "version": 1, + "parameters": {}, + "mapper": { + "body": "{{4.data}}", + "status": "200", + "headers": [] + }, + "metadata": { + "designer": { + "x": 1550, + "y": -293 + }, + "restore": { + "expect": { + "headers": { + "mode": "chose" + } + } + }, + "expect": [ + { + "name": "status", + "type": "uinteger", + "label": "Status", + "required": true, + "validate": { + "min": 100 + } + }, + { + "name": "body", + "type": "any", + "label": "Body" + }, + { + "name": "headers", + "spec": [ + { + "name": "key", + "type": "text", + "label": "Key", + "required": true, + "validate": { + "max": 256 + } + }, + { + "name": "value", + "type": "text", + "label": "Value", + "required": true, + "validate": { + "max": 4096 + } + } + ], + "type": "array", + "label": "Custom headers", + "validate": { + "maxItems": 16 + } + } + ], + "advanced": true + } + } + ] + } + ] + } + ], + "metadata": { + "instant": true, + "version": 1, + "scenario": { + "roundtrips": 1, + "maxErrors": 3, + "autoCommit": true, + "autoCommitTriggerLast": true, + "sequential": false, + "slots": null, + "confidential": false, + "dataloss": false, + "dlq": false, + "freshVariables": false + }, + "designer": { + "orphans": [] + }, + "zone": "us1.make.celonis.com", + "notes": [] + } +} \ No newline at end of file diff --git a/INSTALL.md b/INSTALL.md new file mode 100644 index 0000000..effaaab --- /dev/null +++ b/INSTALL.md @@ -0,0 +1,285 @@ +# Installation Guide - Meta File Server Query Application + +This guide will help you install and run the Meta File Server Query application on different systems. + +## Prerequisites + +### Required Software +- **Python 3.7 or higher** - [Download Python](https://www.python.org/downloads/) +- **Git** (optional) - For cloning the repository +- **Modern web browser** - Chrome, Firefox, Safari, or Edge + +### System Requirements +- **RAM**: 512MB minimum, 1GB recommended +- **Storage**: 100MB free space +- **Network**: Internet access to reach metafile.oliver.solutions + +## Installation Methods + +### Method 1: Quick Install (Recommended) + +1. **Download/Copy the application folder** to your desired location +2. **Open Terminal/Command Prompt** and navigate to the folder: + ```bash + cd /path/to/META-FILE-SERVER-QUERY + ``` +3. **Run the startup script**: + ```bash + ./start.sh + ``` + This will automatically: + - Create the virtual environment + - Install all dependencies + - Start the application + +### Method 2: Manual Installation + +1. **Navigate to the application directory**: + ```bash + cd /path/to/META-FILE-SERVER-QUERY + ``` + +2. **Create Python virtual environment**: + ```bash + python3 -m venv meta-server-venv + ``` + +3. **Activate the virtual environment**: + + **On macOS/Linux**: + ```bash + source meta-server-venv/bin/activate + ``` + + **On Windows**: + ```cmd + meta-server-venv\Scripts\activate + ``` + +4. **Install dependencies**: + ```bash + pip install -r requirements.txt + ``` + +5. **Run the application**: + ```bash + python run.py + ``` + +## Platform-Specific Instructions + +### macOS + +1. **Install Python 3** (if not already installed): + ```bash + # Using Homebrew (recommended) + brew install python3 + + # Or download from python.org + ``` + +2. **Make startup script executable**: + ```bash + chmod +x start.sh + ``` + +3. **Run the application**: + ```bash + ./start.sh + ``` + +### Windows + +1. **Install Python 3** from [python.org](https://www.python.org/downloads/windows/) + - ✅ Check "Add Python to PATH" during installation + +2. **Open Command Prompt or PowerShell**: + ```cmd + cd C:\path\to\META-FILE-SERVER-QUERY + ``` + +3. **Create virtual environment**: + ```cmd + python -m venv meta-server-venv + ``` + +4. **Activate virtual environment**: + ```cmd + meta-server-venv\Scripts\activate + ``` + +5. **Install dependencies**: + ```cmd + pip install -r requirements.txt + ``` + +6. **Run application**: + ```cmd + python run.py + ``` + +### Linux (Ubuntu/Debian) + +1. **Install Python 3 and pip**: + ```bash + sudo apt update + sudo apt install python3 python3-pip python3-venv + ``` + +2. **Navigate and setup**: + ```bash + cd /path/to/META-FILE-SERVER-QUERY + chmod +x start.sh + ./start.sh + ``` + +### Linux (CentOS/RHEL/Fedora) + +1. **Install Python 3**: + ```bash + # CentOS/RHEL + sudo yum install python3 python3-pip + + # Fedora + sudo dnf install python3 python3-pip + ``` + +2. **Run setup**: + ```bash + cd /path/to/META-FILE-SERVER-QUERY + chmod +x start.sh + ./start.sh + ``` + +## Docker Installation (Optional) + +If you prefer using Docker: + +1. **Create Dockerfile**: + ```dockerfile + FROM python:3.9-slim + + WORKDIR /app + COPY . /app + + RUN pip install -r requirements.txt + + EXPOSE 5000 + + CMD ["python", "run.py"] + ``` + +2. **Build and run**: + ```bash + docker build -t meta-file-server . + docker run -p 5000:5000 meta-file-server + ``` + +## Verification + +After installation, verify the application is working: + +1. **Check the startup output** - you should see: + ``` + Meta File Server Query Application + ==================================== + Starting server on http://localhost:5000 + Press Ctrl+C to stop the server + ``` + +2. **Open your web browser** and go to: `http://localhost:5000` + +3. **Test with a job number** (e.g., `6046034`) + +4. **Check the API directly**: `http://localhost:5000/api/health` + +## Troubleshooting Installation + +### Python Not Found +**Error**: `python3: command not found` + +**Solution**: +- Install Python 3 from python.org +- On Windows, try `python` instead of `python3` +- Make sure Python is added to your system PATH + +### Permission Denied +**Error**: `Permission denied: ./start.sh` + +**Solution**: +```bash +chmod +x start.sh +``` + +### Module Not Found +**Error**: `ModuleNotFoundError: No module named 'flask'` + +**Solution**: +- Make sure you activated the virtual environment +- Run: `pip install -r requirements.txt` + +### Port Already in Use +**Error**: `OSError: [Errno 48] Address already in use` + +**Solution**: +- Stop any other applications using port 5000 +- Or modify `run.py` to use a different port: + ```python + app.run(debug=True, host='0.0.0.0', port=5001) # Change port + ``` + +### SSL Certificate Warnings +**Warning**: `InsecureRequestWarning: Unverified HTTPS request` + +**This is expected** - The application disables SSL verification for the metafile server due to certificate issues. This is safe for this specific use case. + +## Uninstallation + +To remove the application: + +1. **Stop the application** (Ctrl+C) +2. **Delete the entire folder**: + ```bash + rm -rf /path/to/META-FILE-SERVER-QUERY + ``` + +The application is self-contained in its directory and doesn't install anything system-wide. + +## Moving to Another Machine + +To transfer the application to another computer: + +1. **Copy the entire META-FILE-SERVER-QUERY folder** +2. **Install Python 3** on the target machine +3. **Run the installation** using Method 1 or 2 above + +The `meta-server-venv` folder can be deleted before copying - it will be recreated during installation. + +## Production Deployment + +For production use on a server: + +1. **Install using Method 2** (manual installation) +2. **Install a production WSGI server**: + ```bash + pip install gunicorn + ``` +3. **Run with Gunicorn**: + ```bash + gunicorn -w 4 -b 0.0.0.0:5000 app:app + ``` +4. **Set up reverse proxy** (nginx/Apache) and SSL certificate +5. **Configure firewall** to allow traffic on your chosen port + +## Support + +If you encounter issues during installation: + +1. Check the **Troubleshooting** section above +2. Verify your Python version: `python3 --version` +3. Check that all required files are present in the application directory +4. Ensure you have internet connectivity to download dependencies + +--- + +**Need help?** The application includes comprehensive error messages and logging to help diagnose issues. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..ebe98b7 --- /dev/null +++ b/README.md @@ -0,0 +1,151 @@ +# Meta File Server Query Application + +A Python Flask web application that replicates the Make.com workflow for querying the Oliver metafile server to retrieve job information based on deliverable numbers. + +## Features + +- **Two-stage workflow**: First fetches XML to extract client information, then retrieves JSON data +- **Secure authentication**: Uses MD5 checksum authentication matching the original blueprint +- **Clean web interface**: Simple HTML form with responsive design +- **Error handling**: Comprehensive error handling for network issues, SSL problems, and parsing errors +- **Portable**: Self-contained with virtual environment for easy deployment + +## Quick Start + +### Option 1: Use the startup script +```bash +./start.sh +``` + +### Option 2: Manual setup +```bash +# Create and activate virtual environment +python3 -m venv meta-server-venv +source meta-server-venv/bin/activate + +# Install dependencies +pip install -r requirements.txt + +# Run the application +python run.py +``` + +Then open your browser to: `http://localhost:5000` + +## Usage + +1. Enter a job/deliverable number in the web form +2. Click "Query Job Information" +3. The application will: + - Fetch the XML file from the metafile server + - Extract the client name from the jobpath + - Retrieve the corresponding JSON data using the client information + - Display the results in a formatted view + +## API Endpoints + +- `GET /` - Main web interface +- `GET /api/job/` - Retrieve job information as JSON +- `GET /api/health` - Health check endpoint + +## Example API Response + +```json +{ + "success": true, + "job_number": "6046034", + "client": "ADIDAS", + "data": { + "JobSpecification": { + // ... job data ... + } + } +} +``` + +## Configuration + +The application uses these default settings: + +- **Metafile Server**: `https://metafile.oliver.solutions/getFile` +- **API Key**: `$14W0TF~8FL` (from original blueprint) +- **Port**: 5000 +- **SSL Verification**: Disabled (due to certificate issues) + +## Project Structure + +``` +META-FILE-SERVER-QUERY/ +├── app.py # Main Flask application +├── run.py # Application runner +├── start.sh # Startup script +├── requirements.txt # Python dependencies +├── templates/ +│ └── index.html # Web interface +├── static/ # Static assets (empty) +└── meta-server-venv/ # Python virtual environment +``` + +## Technical Details + +### Workflow Process + +1. **XML Retrieval**: + - Constructs URL: `{base_url}?root=XML&path={job_number}.xml&keyid=1&cs={checksum}` + - Checksum: `MD5(api_key + "XML/" + filename)` + +2. **Client Extraction**: + - Parses XML for `Set-Property` with `Property="jobpath"` + - Extracts client name using regex: `([A-Z][A-Z0-9_]*?)(?=/CAMPAIGNS/)` + +3. **JSON Retrieval**: + - Constructs URL: `{base_url}?root=JSON_STORE&sub={client}&path={job_number}.json&keyid=1&cs={checksum}` + - Checksum: `MD5(api_key + "JSON_STORE/" + client + "/" + filename)` + +### Error Handling + +- SSL certificate verification disabled for metafile.oliver.solutions +- Network timeout protection (30 seconds) +- XML parsing error handling +- JSON parsing error handling +- HTTP status code validation + +## Moving to Production + +To deploy this application: + +1. **Copy the entire directory** to your target server +2. **Install Python 3.7+** on the target system +3. **Run the startup script**: `./start.sh` +4. **For production**: Consider using a WSGI server like Gunicorn: + ```bash + pip install gunicorn + gunicorn -w 4 -b 0.0.0.0:5000 app:app + ``` + +## Troubleshooting + +### SSL Certificate Issues +The application automatically handles SSL certificate verification issues by disabling verification. This is necessary because the metafile server has certificate problems. + +### Connection Timeouts +The application has a 30-second timeout for requests. If you experience frequent timeouts, check your network connection to the metafile server. + +### Missing Job Data +If a job number returns an error, verify: +1. The job number exists in the XML system +2. The corresponding JSON file exists in the JSON_STORE +3. The client name extraction is working correctly + +## Development + +To modify or extend the application: + +1. Activate the virtual environment: `source meta-server-venv/bin/activate` +2. Make your changes to `app.py` or `templates/index.html` +3. Test your changes: `python run.py` +4. The server will automatically reload in debug mode + +--- + +*Converted from Make.com blueprint: "AI COMPANION Metafile Server Grabber"* \ No newline at end of file diff --git a/app.py b/app.py new file mode 100644 index 0000000..60d58b1 --- /dev/null +++ b/app.py @@ -0,0 +1,197 @@ +import hashlib +import re +import requests +import xml.etree.ElementTree as ET +from flask import Flask, render_template, jsonify, request +from urllib.parse import quote +import urllib3 + +# Disable SSL warnings when using verify=False +urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) + +app = Flask(__name__) + +# Configuration +METAFILE_BASE_URL = "https://metafile.oliver.solutions/getFile" +METAFILE_KEY = "$14W0TF~8FL" + +class MetafileClient: + def __init__(self, base_url, api_key): + self.base_url = base_url + self.api_key = api_key + + def _generate_checksum(self, root, path, sub=None): + """Generate MD5 checksum for metafile API authentication""" + if sub: + checksum_string = f"{self.api_key}{root}/{sub}/{path}" + else: + checksum_string = f"{self.api_key}{root}/{path}" + return hashlib.md5(checksum_string.encode('utf-8')).hexdigest() + + def get_file(self, root, path, keyid=1, sub=None): + """Make request to metafile server""" + checksum = self._generate_checksum(root, path, sub) + + params = { + 'root': root, + 'path': path, + 'keyid': keyid, + 'cs': checksum + } + + if sub: + params['sub'] = sub + + try: + response = requests.get(self.base_url, params=params, timeout=30, verify=False) + response.raise_for_status() + return { + 'success': True, + 'data': response.text, + 'status_code': response.status_code + } + except requests.exceptions.RequestException as e: + return { + 'success': False, + 'error': str(e), + 'status_code': getattr(e.response, 'status_code', None) if hasattr(e, 'response') else None + } + + def get_client_from_xml(self, deliverable_number): + """Get client information from XML file""" + xml_filename = f"{deliverable_number}.xml" + result = self.get_file(root="XML", path=xml_filename) + + if not result['success']: + return result + + try: + # Parse XML + root_elem = ET.fromstring(result['data']) + + # Look for Set-Property elements with Property="jobpath" + jobpath_elements = root_elem.findall(".//Set-Property[@Property='jobpath']") + + for prop in jobpath_elements: + jobpath_value = prop.get('Value', '') + if jobpath_value and '/CAMPAIGNS/' in jobpath_value: + # Extract client name from jobpath like "/ADIDAS_CAM_OLIVER_KUALA_LUMPUR/ADIDAS/CAMPAIGNS/..." + # Use regex to extract client name before /CAMPAIGNS + client_pattern = r'([A-Z][A-Z0-9_]*?)(?=/CAMPAIGNS/)' + match = re.search(client_pattern, jobpath_value) + if match: + return { + 'success': True, + 'client': match.group(1) + } + + # Fallback: look for any Value containing /CAMPAIGNS/ + set_properties = root_elem.findall(".//Set-Property[@Value]") + for prop in set_properties: + value = prop.get('Value', '') + if '/CAMPAIGNS/' in value: + client_pattern = r'([A-Z][A-Z0-9_]*?)(?=/CAMPAIGNS/)' + match = re.search(client_pattern, value) + if match: + return { + 'success': True, + 'client': match.group(1) + } + + return { + 'success': False, + 'error': 'Could not extract client information from XML' + } + + except ET.ParseError as e: + return { + 'success': False, + 'error': f'XML parsing error: {str(e)}' + } + except Exception as e: + return { + 'success': False, + 'error': f'Error processing XML: {str(e)}' + } + + def get_job_data(self, deliverable_number): + """Get complete job data (XML + JSON workflow)""" + # Step 1: Get client from XML + client_result = self.get_client_from_xml(deliverable_number) + + if not client_result['success']: + return client_result + + client = client_result['client'] + + # Step 2: Get JSON data using client info + json_filename = f"{deliverable_number}.json" + json_result = self.get_file(root="JSON_STORE", path=json_filename, sub=client) + + if not json_result['success']: + return json_result + + # Check if response is 200 OK + if json_result['status_code'] != 200: + return { + 'success': False, + 'error': f'JSON file not found or inaccessible (HTTP {json_result["status_code"]})' + } + + try: + import json + json_data = json.loads(json_result['data']) + return { + 'success': True, + 'client': client, + 'data': json_data + } + except json.JSONDecodeError as e: + return { + 'success': False, + 'error': f'JSON parsing error: {str(e)}' + } + +# Initialize metafile client +metafile_client = MetafileClient(METAFILE_BASE_URL, METAFILE_KEY) + +@app.route('/') +def index(): + """Serve the main HTML page""" + return render_template('index.html') + +@app.route('/api/job/') +def get_job_info(job_number): + """API endpoint to get job information""" + if not job_number: + return jsonify({'error': 'Job number is required'}), 400 + + # Sanitize job number (remove any non-alphanumeric characters except hyphens and underscores) + job_number = re.sub(r'[^a-zA-Z0-9\-_]', '', job_number) + + if not job_number: + return jsonify({'error': 'Invalid job number format'}), 400 + + result = metafile_client.get_job_data(job_number) + + if result['success']: + return jsonify({ + 'success': True, + 'job_number': job_number, + 'client': result['client'], + 'data': result['data'] + }) + else: + return jsonify({ + 'success': False, + 'job_number': job_number, + 'error': result['error'] + }), 400 + +@app.route('/api/health') +def health_check(): + """Health check endpoint""" + return jsonify({'status': 'healthy', 'service': 'Meta File Server Query'}) + +if __name__ == '__main__': + app.run(debug=True, host='0.0.0.0', port=5000) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..839f148 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,13 @@ +blinker==1.9.0 +certifi==2025.8.3 +charset-normalizer==3.4.3 +click==8.2.1 +Flask==3.1.2 +idna==3.10 +itsdangerous==2.2.0 +Jinja2==3.1.6 +lxml==6.0.1 +MarkupSafe==3.0.2 +requests==2.32.5 +urllib3==2.5.0 +Werkzeug==3.1.3 diff --git a/run.py b/run.py new file mode 100644 index 0000000..991f737 --- /dev/null +++ b/run.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +""" +Meta File Server Query Application + +A simple Flask application that replicates the Make.com workflow for querying +the Oliver metafile server to retrieve job information based on deliverable numbers. + +Usage: + python run.py + +The server will start on http://localhost:5000 +""" + +import os +import sys + +# Add the current directory to Python path +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +from app import app + +if __name__ == '__main__': + print("=" * 60) + print("Meta File Server Query Application") + print("=" * 60) + print("Starting server on http://localhost:5000") + print("Press Ctrl+C to stop the server") + print("=" * 60) + + try: + app.run(debug=True, host='0.0.0.0', port=5000) + except KeyboardInterrupt: + print("\n\nServer stopped by user.") + sys.exit(0) \ No newline at end of file diff --git a/start.sh b/start.sh new file mode 100755 index 0000000..d1ac433 --- /dev/null +++ b/start.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# Meta File Server Query - Start Script +echo "Starting Meta File Server Query Application..." + +# Check if virtual environment exists +if [ ! -d "meta-server-venv" ]; then + echo "Creating virtual environment..." + python3 -m venv meta-server-venv + echo "Installing dependencies..." + source meta-server-venv/bin/activate + pip install -r requirements.txt +else + echo "Activating virtual environment..." + source meta-server-venv/bin/activate +fi + +echo "Starting Flask application..." +python run.py \ No newline at end of file diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..984522c --- /dev/null +++ b/templates/index.html @@ -0,0 +1,272 @@ + + + + + + Meta File Server Query + + + +
+

Meta File Server Query

+ +
+
+ + +
+ +
+ + +
+
+ +
+

🔍 Querying meta file server...

+
+ +
+
+
+
+ + + + \ No newline at end of file