From 537994b4e28b00dfb741721da8bb9cb5f57fc738 Mon Sep 17 00:00:00 2001 From: Max Litruv Boonzaayer Date: Fri, 17 Apr 2026 03:34:32 +1000 Subject: [PATCH] Remove obsolete server files - using Gitea Actions instead --- .env.example | 9 - package-lock.json | 920 --------------------------------------- package.json | 19 - src/Validator.js | 193 -------- src/ValidatorEnhanced.js | 232 ---------- src/WebhookHandler.js | 245 ----------- src/server.js | 86 ---- 7 files changed, 1704 deletions(-) delete mode 100644 .env.example delete mode 100644 package-lock.json delete mode 100644 package.json delete mode 100644 src/Validator.js delete mode 100644 src/ValidatorEnhanced.js delete mode 100644 src/WebhookHandler.js delete mode 100644 src/server.js diff --git a/.env.example b/.env.example deleted file mode 100644 index 2baa43d..0000000 --- a/.env.example +++ /dev/null @@ -1,9 +0,0 @@ -# Gitea configuration -GITEA_URL=http://synbox.ruv.wtf:8418 -GITEA_TOKEN=your_gitea_access_token_here - -# Webhook secret (must match Gitea webhook configuration) -WEBHOOK_SECRET=your_webhook_secret_here - -# Server port -PORT=3000 diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 1f721af..0000000 --- a/package-lock.json +++ /dev/null @@ -1,920 +0,0 @@ -{ - "name": "@pluginhost/directory", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "@pluginhost/directory", - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "express": "^4.18.2", - "node-fetch": "^3.3.2" - } - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "license": "MIT" - }, - "node_modules/body-parser": { - "version": "1.20.4", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", - "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", - "license": "MIT", - "dependencies": { - "bytes": "~3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "~1.2.0", - "http-errors": "~2.0.1", - "iconv-lite": "~0.4.24", - "on-finished": "~2.4.1", - "qs": "~6.14.0", - "raw-body": "~2.5.3", - "type-is": "~1.6.18", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", - "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", - "license": "MIT" - }, - "node_modules/data-uri-to-buffer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", - "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "license": "MIT", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", - "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "~1.20.3", - "content-disposition": "~0.5.4", - "content-type": "~1.0.4", - "cookie": "~0.7.1", - "cookie-signature": "~1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.3.1", - "fresh": "~0.5.2", - "http-errors": "~2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "~2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "~0.1.12", - "proxy-addr": "~2.0.7", - "qs": "~6.14.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "~0.19.0", - "serve-static": "~1.16.2", - "setprototypeof": "1.2.0", - "statuses": "~2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/fetch-blob": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", - "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "paypal", - "url": "https://paypal.me/jimmywarting" - } - ], - "license": "MIT", - "dependencies": { - "node-domexception": "^1.0.0", - "web-streams-polyfill": "^3.0.3" - }, - "engines": { - "node": "^12.20 || >= 14.13" - } - }, - "node_modules/finalhandler": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", - "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "~2.4.1", - "parseurl": "~1.3.3", - "statuses": "~2.0.2", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/formdata-polyfill": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", - "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", - "license": "MIT", - "dependencies": { - "fetch-blob": "^3.1.2" - }, - "engines": { - "node": ">=12.20.0" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", - "license": "MIT", - "dependencies": { - "depd": "~2.0.0", - "inherits": "~2.0.4", - "setprototypeof": "~1.2.0", - "statuses": "~2.0.2", - "toidentifier": "~1.0.1" - }, - "engines": { - "node": ">= 0.8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "deprecated": "Use your platform's native DOMException instead", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "github", - "url": "https://paypal.me/jimmywarting" - } - ], - "license": "MIT", - "engines": { - "node": ">=10.5.0" - } - }, - "node_modules/node-fetch": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", - "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", - "license": "MIT", - "dependencies": { - "data-uri-to-buffer": "^4.0.0", - "fetch-blob": "^3.1.4", - "formdata-polyfill": "^4.0.10" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/node-fetch" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-to-regexp": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", - "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", - "license": "MIT" - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/qs": { - "version": "6.14.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", - "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", - "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", - "license": "MIT", - "dependencies": { - "bytes": "~3.1.2", - "http-errors": "~2.0.1", - "iconv-lite": "~0.4.24", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/send": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", - "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "~0.5.2", - "http-errors": "~2.0.1", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "~2.4.1", - "range-parser": "~1.2.1", - "statuses": "~2.0.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/serve-static": { - "version": "1.16.3", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", - "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", - "license": "MIT", - "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "~0.19.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", - "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/web-streams-polyfill": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", - "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index 8892c6b..0000000 --- a/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "@pluginhost/directory", - "version": "1.0.0", - "description": "Automated plugin directory with PR validation and auto-merge", - "main": "src/server.js", - "type": "module", - "scripts": { - "start": "node src/server.js", - "dev": "node --watch src/server.js", - "validate": "node src/validate.js" - }, - "keywords": ["plugin", "directory", "registry"], - "author": "", - "license": "MIT", - "dependencies": { - "express": "^4.18.2", - "node-fetch": "^3.3.2" - } -} diff --git a/src/Validator.js b/src/Validator.js deleted file mode 100644 index 2f214c9..0000000 --- a/src/Validator.js +++ /dev/null @@ -1,193 +0,0 @@ -import { readFile } from 'fs/promises'; - -/** - * Validator - Validates PR changes against plugin directory rules - */ -export class Validator { - constructor() { - this.requiredFields = ['id', 'name', 'version', 'description', 'author', 'repository']; - this.optionalFields = ['downloadUrl', 'homepage', 'tags', 'addedDate', 'dependencies']; - } - - /** - * Validate a PR's file changes - */ - async validatePR(files, prAuthor) { - const errors = []; - const added = []; - const removed = []; - - // Check that all files are in the plugins/ directory - const invalidPaths = files.filter(f => !f.filename.startsWith('plugins/')); - if (invalidPaths.length > 0) { - errors.push(`Files outside plugins/ directory: ${invalidPaths.map(f => f.filename).join(', ')}`); - } - - // Check that all files are .json files - const nonJsonFiles = files.filter(f => - f.filename.startsWith('plugins/') && !f.filename.endsWith('.json') - ); - if (nonJsonFiles.length > 0) { - errors.push(`Non-JSON files in plugins/: ${nonJsonFiles.map(f => f.filename).join(', ')}`); - } - - // Process each changed file - for (const file of files) { - if (!file.filename.startsWith('plugins/') || !file.filename.endsWith('.json')) { - continue; - } - - const pluginId = file.filename.replace('plugins/', '').replace('.json', ''); - - if (file.status === 'added' || file.status === 'modified') { - // Validate added/modified plugins - try { - const validation = await this.validatePluginFile(file, pluginId, prAuthor); - - if (!validation.valid) { - errors.push(...validation.errors.map(e => `${file.filename}: ${e}`)); - } else { - added.push(pluginId); - } - } catch (error) { - errors.push(`${file.filename}: ${error.message}`); - } - } else if (file.status === 'removed') { - // Validate removed plugins (check ownership) - try { - const canRemove = await this.validateRemoval(pluginId, prAuthor); - - if (!canRemove.valid) { - errors.push(...canRemove.errors.map(e => `${file.filename}: ${e}`)); - } else { - removed.push(pluginId); - } - } catch (error) { - errors.push(`${file.filename}: ${error.message}`); - } - } - } - - // Must have at least one change - if (files.length === 0) { - errors.push('PR has no file changes'); - } - - return { - valid: errors.length === 0, - errors, - added, - removed - }; - } - - /** - * Validate a plugin JSON file - */ - async validatePluginFile(file, pluginId, prAuthor) { - const errors = []; - - try { - // Parse the JSON content from the file patch - const content = this.extractFileContent(file); - const plugin = JSON.parse(content); - - // Check required fields - for (const field of this.requiredFields) { - if (!plugin[field]) { - errors.push(`Missing required field: ${field}`); - } - } - - // Check that ID matches filename - if (plugin.id !== pluginId) { - errors.push(`Plugin ID "${plugin.id}" does not match filename "${pluginId}.json"`); - } - - // Validate version format (semver-ish) - if (plugin.version && !this.isValidVersion(plugin.version)) { - errors.push(`Invalid version format: ${plugin.version} (expected: X.Y.Z)`); - } - - // Validate repository URL - if (plugin.repository && !this.isValidUrl(plugin.repository)) { - errors.push(`Invalid repository URL: ${plugin.repository}`); - } - - // Validate author matches PR author (for new plugins) - if (file.status === 'added' && plugin.author !== prAuthor) { - errors.push(`Author "${plugin.author}" must match PR author "${prAuthor}"`); - } - - // Validate tags if present - if (plugin.tags && !Array.isArray(plugin.tags)) { - errors.push('Tags must be an array'); - } - - } catch (error) { - errors.push(`Invalid JSON: ${error.message}`); - } - - return { - valid: errors.length === 0, - errors - }; - } - - /** - * Validate that a user can remove a plugin - */ - async validateRemoval(pluginId, prAuthor) { - const errors = []; - - try { - // Read the existing plugin file from disk - const content = await readFile(`./plugins/${pluginId}.json`, 'utf-8'); - const plugin = JSON.parse(content); - - // Check if the PR author is the plugin author - if (plugin.author !== prAuthor) { - errors.push(`Cannot remove plugin: you are not the author (author: ${plugin.author})`); - } - } catch (error) { - // If file doesn't exist, it's probably already removed - console.warn(`Could not validate removal of ${pluginId}:`, error.message); - } - - return { - valid: errors.length === 0, - errors - }; - } - - /** - * Extract file content from Gitea file object - * In a real implementation, you'd fetch the raw content from the PR - */ - extractFileContent(file) { - // This is a simplified version - // In production, fetch the actual file content from the PR's head branch - - // For now, return a placeholder that would need to be fetched - throw new Error('File content extraction not fully implemented - fetch from PR branch'); - } - - /** - * Validate semantic version format - */ - isValidVersion(version) { - return /^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$/.test(version); - } - - /** - * Validate URL format - */ - isValidUrl(url) { - try { - new URL(url); - return true; - } catch { - return false; - } - } -} diff --git a/src/ValidatorEnhanced.js b/src/ValidatorEnhanced.js deleted file mode 100644 index 0daffd1..0000000 --- a/src/ValidatorEnhanced.js +++ /dev/null @@ -1,232 +0,0 @@ -import { WebhookHandler } from './WebhookHandler.js'; -import fetch from 'node-fetch'; - -/** - * Enhanced validator that fetches actual file content from PR - */ -export class Validator { - constructor(webhookHandler) { - this.handler = webhookHandler; - this.requiredFields = ['id', 'name', 'version', 'description', 'author', 'repository']; - this.optionalFields = ['downloadUrl', 'homepage', 'tags', 'addedDate', 'dependencies']; - } - - /** - * Validate a PR's file changes - */ - async validatePR(files, prAuthor, prNumber) { - const errors = []; - const added = []; - const removed = []; - - // Check that all files are in the plugins/ directory - const invalidPaths = files.filter(f => !f.filename.startsWith('plugins/')); - if (invalidPaths.length > 0) { - errors.push(`Files outside plugins/ directory: ${invalidPaths.map(f => f.filename).join(', ')}`); - } - - // Check that all files are .json files - const nonJsonFiles = files.filter(f => - f.filename.startsWith('plugins/') && !f.filename.endsWith('.json') - ); - if (nonJsonFiles.length > 0) { - errors.push(`Non-JSON files in plugins/: ${nonJsonFiles.map(f => f.filename).join(', ')}`); - } - - // Process each changed file - for (const file of files) { - if (!file.filename.startsWith('plugins/') || !file.filename.endsWith('.json')) { - continue; - } - - const pluginId = file.filename.replace('plugins/', '').replace('.json', ''); - - if (file.status === 'added' || file.status === 'modified') { - // Fetch and validate the file content - try { - const content = await this.fetchPRFileContent(prNumber, file.filename); - const validation = await this.validatePluginContent(content, pluginId, prAuthor, file.status); - - if (!validation.valid) { - errors.push(...validation.errors.map(e => `${file.filename}: ${e}`)); - } else { - added.push(pluginId); - } - } catch (error) { - errors.push(`${file.filename}: ${error.message}`); - } - } else if (file.status === 'removed') { - // Validate removed plugins (check ownership) - try { - const canRemove = await this.validateRemoval(pluginId, prAuthor); - - if (!canRemove.valid) { - errors.push(...canRemove.errors.map(e => `${file.filename}: ${e}`)); - } else { - removed.push(pluginId); - } - } catch (error) { - errors.push(`${file.filename}: ${error.message}`); - } - } - } - - // Must have at least one change - if (files.length === 0) { - errors.push('PR has no file changes'); - } - - return { - valid: errors.length === 0, - errors, - added, - removed - }; - } - - /** - * Fetch file content from a PR - */ - async fetchPRFileContent(prNumber, filename) { - const pr = await this.getPRDetails(prNumber); - const headSha = pr.head.sha; - - const url = `${this.handler.giteaUrl}/api/v1/repos/${this.handler.repoOwner}/${this.handler.repoName}/contents/${filename}?ref=${headSha}`; - - const response = await fetch(url, { - headers: { 'Authorization': `token ${this.handler.giteaToken}` } - }); - - if (!response.ok) { - throw new Error(`Failed to fetch file: ${response.statusText}`); - } - - const data = await response.json(); - const content = Buffer.from(data.content, 'base64').toString('utf-8'); - return content; - } - - /** - * Get PR details - */ - async getPRDetails(prNumber) { - const url = `${this.handler.giteaUrl}/api/v1/repos/${this.handler.repoOwner}/${this.handler.repoName}/pulls/${prNumber}`; - - const response = await fetch(url, { - headers: { 'Authorization': `token ${this.handler.giteaToken}` } - }); - - if (!response.ok) { - throw new Error(`Failed to fetch PR: ${response.statusText}`); - } - - return await response.json(); - } - - /** - * Validate plugin JSON content - */ - async validatePluginContent(content, pluginId, prAuthor, status) { - const errors = []; - - try { - const plugin = JSON.parse(content); - - // Check required fields - for (const field of this.requiredFields) { - if (!plugin[field]) { - errors.push(`Missing required field: ${field}`); - } - } - - // Check that ID matches filename - if (plugin.id !== pluginId) { - errors.push(`Plugin ID "${plugin.id}" does not match filename "${pluginId}.json"`); - } - - // Validate version format (semver-ish) - if (plugin.version && !this.isValidVersion(plugin.version)) { - errors.push(`Invalid version format: ${plugin.version} (expected: X.Y.Z)`); - } - - // Validate repository URL - if (plugin.repository && !this.isValidUrl(plugin.repository)) { - errors.push(`Invalid repository URL: ${plugin.repository}`); - } - - // Validate author matches PR author (for new plugins) - if (status === 'added' && plugin.author !== prAuthor) { - errors.push(`Author "${plugin.author}" must match PR author "${prAuthor}"`); - } - - // Validate tags if present - if (plugin.tags && !Array.isArray(plugin.tags)) { - errors.push('Tags must be an array'); - } - - } catch (error) { - errors.push(`Invalid JSON: ${error.message}`); - } - - return { - valid: errors.length === 0, - errors - }; - } - - /** - * Validate that a user can remove a plugin - */ - async validateRemoval(pluginId, prAuthor) { - const errors = []; - - try { - // Read the existing plugin file from main branch - const url = `${this.handler.giteaUrl}/api/v1/repos/${this.handler.repoOwner}/${this.handler.repoName}/contents/plugins/${pluginId}.json`; - - const response = await fetch(url, { - headers: { 'Authorization': `token ${this.handler.giteaToken}` } - }); - - if (!response.ok) { - // If file doesn't exist on main, removal is fine - return { valid: true, errors: [] }; - } - - const data = await response.json(); - const content = Buffer.from(data.content, 'base64').toString('utf-8'); - const plugin = JSON.parse(content); - - // Check if the PR author is the plugin author - if (plugin.author !== prAuthor) { - errors.push(`Cannot remove plugin: you are not the author (author: ${plugin.author})`); - } - } catch (error) { - console.warn(`Could not validate removal of ${pluginId}:`, error.message); - } - - return { - valid: errors.length === 0, - errors - }; - } - - /** - * Validate semantic version format - */ - isValidVersion(version) { - return /^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$/.test(version); - } - - /** - * Validate URL format - */ - isValidUrl(url) { - try { - new URL(url); - return true; - } catch { - return false; - } - } -} diff --git a/src/WebhookHandler.js b/src/WebhookHandler.js deleted file mode 100644 index 019af9c..0000000 --- a/src/WebhookHandler.js +++ /dev/null @@ -1,245 +0,0 @@ -import crypto from 'crypto'; -import fetch from 'node-fetch'; -import { readdir, readFile } from 'fs/promises'; -import { join, dirname, resolve } from 'path'; -import { fileURLToPath } from 'url'; -import { Validator } from './ValidatorEnhanced.js'; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -/** - * WebhookHandler - Processes Gitea webhooks for automated PR handling - */ -export class WebhookHandler { - constructor(options) { - this.giteaUrl = options.giteaUrl; - this.giteaToken = options.giteaToken; - this.webhookSecret = options.webhookSecret; - this.repoOwner = options.repoOwner; - this.repoName = options.repoName; - this.validator = new Validator(this); - } - - /** - * Verify webhook signature - */ - verifySignature(payload, signature) { - if (!this.webhookSecret) return true; // Skip if no secret configured - - const hmac = crypto.createHmac('sha256', this.webhookSecret); - const digest = hmac.update(JSON.stringify(payload)).digest('hex'); - return signature === digest; - } - - /** - * Handle pull request webhook - */ - async handlePullRequest(payload) { - const { action, pull_request, repository } = payload; - - console.log(`PR #${pull_request.number}: ${action} by ${pull_request.user.login}`); - - // Only process opened or synchronized PRs - if (action !== 'opened' && action !== 'synchronize') { - console.log('Skipping PR action:', action); - return; - } - - try { - // Get PR details - const prNumber = pull_request.number; - const prAuthor = pull_request.user.login; - const headBranch = pull_request.head.ref; - const baseBranch = pull_request.base.ref; - - // Get file changes in the PR - const files = await this.getPRFiles(prNumber); - - console.log(`PR files changed: ${files.map(f => `${f.filename} (${f.status})`).join(', ')}`); - - // Validate the PR - const validation = await this.validator.validatePR(files, prAuthor, prNumber); - - if (!validation.valid) { - console.log('Validation failed:', validation.errors); - await this.commentOnPR(prNumber, this.buildErrorComment(validation.errors)); - await this.labelPR(prNumber, 'validation-failed'); - return; - } - - // PR is valid, auto-approve and merge - console.log('✓ PR validation passed'); - await this.commentOnPR(prNumber, this.buildSuccessComment(validation)); - await this.labelPR(prNumber, 'auto-approved'); - await this.mergePR(prNumber); - - console.log(`✓ Auto-merged PR #${prNumber}`); - } catch (error) { - console.error('Error handling PR:', error); - await this.commentOnPR(pull_request.number, - `❌ **Automation Error**\n\nFailed to process this PR: ${error.message}` - ); - } - } - - /** - * Get files changed in a PR - */ - async getPRFiles(prNumber) { - const url = `${this.giteaUrl}/api/v1/repos/${this.repoOwner}/${this.repoName}/pulls/${prNumber}/files`; - const response = await fetch(url, { - headers: { 'Authorization': `token ${this.giteaToken}` } - }); - - if (!response.ok) { - throw new Error(`Failed to fetch PR files: ${response.statusText}`); - } - - return await response.json(); - } - - /** - * Comment on a PR - */ - async commentOnPR(prNumber, body) { - const url = `${this.giteaUrl}/api/v1/repos/${this.repoOwner}/${this.repoName}/issues/${prNumber}/comments`; - - const response = await fetch(url, { - method: 'POST', - headers: { - 'Authorization': `token ${this.giteaToken}`, - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ body }) - }); - - if (!response.ok) { - console.error('Failed to comment on PR:', response.statusText); - } - } - - /** - * Label a PR - */ - async labelPR(prNumber, label) { - const url = `${this.giteaUrl}/api/v1/repos/${this.repoOwner}/${this.repoName}/issues/${prNumber}/labels`; - - const response = await fetch(url, { - method: 'POST', - headers: { - 'Authorization': `token ${this.giteaToken}`, - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ labels: [label] }) - }); - - if (!response.ok) { - console.error('Failed to label PR:', response.statusText); - } - } - - /** - * Merge a PR - */ - async mergePR(prNumber) { - const url = `${this.giteaUrl}/api/v1/repos/${this.repoOwner}/${this.repoName}/pulls/${prNumber}/merge`; - - const response = await fetch(url, { - method: 'POST', - headers: { - 'Authorization': `token ${this.giteaToken}`, - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - Do: 'squash', - MergeMessageField: 'Auto-merged plugin directory update', - delete_branch_after_merge: true - }) - }); - - if (!response.ok) { - throw new Error(`Failed to merge PR: ${response.statusText}`); - } - - return await response.json(); - } - - /** - * Build error comment for PR - */ - buildErrorComment(errors) { - let comment = '❌ **Validation Failed**\n\nThis PR cannot be auto-merged due to the following issues:\n\n'; - - errors.forEach(error => { - comment += `- ${error}\n`; - }); - - comment += '\n**Rules:**\n'; - comment += '1. Only modify files in the `plugins/` directory\n'; - comment += '2. Only add or remove `.json` files\n'; - comment += '3. JSON files must match the plugin schema\n'; - comment += '4. You can only remove plugins you authored\n'; - comment += '5. Plugin ID must match the filename (without .json)\n'; - comment += '\nPlease fix these issues and push again.'; - - return comment; - } - - /** - * Build success comment for PR - */ - buildSuccessComment(validation) { - let comment = '✅ **Validation Passed**\n\nThis PR has been automatically approved and will be merged.\n\n'; - - if (validation.added.length > 0) { - comment += `**Added plugins:** ${validation.added.join(', ')}\n`; - } - - if (validation.removed.length > 0) { - comment += `**Removed plugins:** ${validation.removed.join(', ')}\n`; - } - - comment += '\nTo undo this change, create a new PR that reverses these changes.'; - - return comment; - } - - /** - * List all plugins from the plugins directory - */ - async listPlugins() { - const pluginsDir = resolve(__dirname, '../plugins'); - const files = await readdir(pluginsDir); - const plugins = []; - - for (const file of files) { - if (file.endsWith('.json')) { - try { - const content = await readFile(join(pluginsDir, file), 'utf-8'); - const plugin = JSON.parse(content); - plugins.push(plugin); - } catch (error) { - console.error(`Error reading plugin ${file}:`, error); - } - } - } - - return plugins; - } - - /** - * Get a specific plugin - */ - async getPlugin(pluginId) { - const pluginsDir = resolve(__dirname, '../plugins'); - const filePath = join(pluginsDir, `${pluginId}.json`); - - try { - const content = await readFile(filePath, 'utf-8'); - return JSON.parse(content); - } catch (error) { - return null; - } - } -} diff --git a/src/server.js b/src/server.js deleted file mode 100644 index 513e1d2..0000000 --- a/src/server.js +++ /dev/null @@ -1,86 +0,0 @@ -import express from 'express'; -import { WebhookHandler } from './WebhookHandler.js'; -import { readFile } from 'fs/promises'; - -const app = express(); -const port = process.env.PORT || 3000; -const webhookSecret = process.env.WEBHOOK_SECRET || ''; - -app.use(express.json()); - -const handler = new WebhookHandler({ - giteaUrl: process.env.GITEA_URL || 'http://synbox.ruv.wtf:8418', - giteaToken: process.env.GITEA_TOKEN || '', - webhookSecret, - repoOwner: 'litruv', - repoName: 'Plugin-Directory' -}); - -/** - * Health check endpoint - */ -app.get('/health', (req, res) => { - res.json({ status: 'ok', timestamp: new Date().toISOString() }); -}); - -/** - * List all plugins endpoint - */ -app.get('/plugins', async (req, res) => { - try { - const plugins = await handler.listPlugins(); - res.json({ plugins, count: plugins.length }); - } catch (error) { - console.error('Error listing plugins:', error); - res.status(500).json({ error: 'Failed to list plugins' }); - } -}); - -/** - * Get specific plugin endpoint - */ -app.get('/plugins/:pluginId', async (req, res) => { - try { - const plugin = await handler.getPlugin(req.params.pluginId); - if (plugin) { - res.json(plugin); - } else { - res.status(404).json({ error: 'Plugin not found' }); - } - } catch (error) { - console.error('Error getting plugin:', error); - res.status(500).json({ error: 'Failed to get plugin' }); - } -}); - -/** - * Webhook endpoint for Gitea PR events - */ -app.post('/webhook', async (req, res) => { - try { - const signature = req.headers['x-gitea-signature']; - const event = req.headers['x-gitea-event']; - - if (!handler.verifySignature(req.body, signature)) { - console.warn('Invalid webhook signature'); - return res.status(401).json({ error: 'Invalid signature' }); - } - - console.log(`Received webhook event: ${event}`); - - if (event === 'pull_request') { - await handler.handlePullRequest(req.body); - } - - res.json({ status: 'processed' }); - } catch (error) { - console.error('Webhook error:', error); - res.status(500).json({ error: 'Webhook processing failed' }); - } -}); - -app.listen(port, () => { - console.log(`Plugin Directory server running on port ${port}`); - console.log(`Webhook endpoint: http://litruv-sub:${port}/webhook`); - console.log(`Plugin list: http://litruv-sub:${port}/plugins`); -});