From ca608c3c41e385c2ff89a5d09f10924744acdb8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=90=8C=E6=A3=AE?= Date: Wed, 7 Aug 2024 13:42:55 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8F=91=E5=B8=83=E5=BA=94=E7=94=A8=20Iframely?= =?UTF-8?q?=20=E5=BA=94=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 萌森 --- apps/iframely/2.4.3/config/config.local.js | 339 +++++++++++++++++++++ apps/iframely/2.4.3/data.yml | 24 ++ apps/iframely/2.4.3/docker-compose.yml | 25 ++ apps/iframely/2.4.3/scripts/init.sh | 15 + apps/iframely/2.4.3/scripts/uninstall.sh | 10 + apps/iframely/2.4.3/scripts/upgrade.sh | 15 + apps/iframely/README.md | 46 +++ apps/iframely/data.yml | 18 ++ apps/iframely/logo.png | Bin 0 -> 14374 bytes 9 files changed, 492 insertions(+) create mode 100644 apps/iframely/2.4.3/config/config.local.js create mode 100644 apps/iframely/2.4.3/data.yml create mode 100644 apps/iframely/2.4.3/docker-compose.yml create mode 100644 apps/iframely/2.4.3/scripts/init.sh create mode 100644 apps/iframely/2.4.3/scripts/uninstall.sh create mode 100644 apps/iframely/2.4.3/scripts/upgrade.sh create mode 100644 apps/iframely/README.md create mode 100644 apps/iframely/data.yml create mode 100644 apps/iframely/logo.png diff --git a/apps/iframely/2.4.3/config/config.local.js b/apps/iframely/2.4.3/config/config.local.js new file mode 100644 index 000000000..d3cc8f436 --- /dev/null +++ b/apps/iframely/2.4.3/config/config.local.js @@ -0,0 +1,339 @@ +import {fileURLToPath} from 'url'; +import {dirname} from 'path'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +// Optional SSL cert, if you serve under HTTPS. +/* +const ssl_key = const file = await readFile(new URL('./key.pem', import.meta.url), {encoding: 'utf8'}); +const ssl_cert = await readFile(new URL('./cert.pem', import.meta.url), {encoding: 'utf8'}); +*/ + +export default { + + // Specify a path for custom plugins. Custom plugins will override core plugins. + // CUSTOM_PLUGINS_PATH: __dirname + '/yourcustom-plugin-folder', + + DEBUG: false, + RICH_LOG_ENABLED: false, + + // For embeds that require render, baseAppUrl will be used as the host. + baseAppUrl: "http://localhost:8061", // use "https://yourdomain.com/path" where you have Iframely in your reverse proxy + relativeStaticUrl: "/r", + + // Or just skip built-in renders altogether + SKIP_IFRAMELY_RENDERS: true, + + // For legacy reasons the response format of Iframely open-source is + // different by default as it does not group the links array by rel. + // In order to get the same grouped response as in Cloud API, + // add `&group=true` to your request to change response per request + // or set `GROUP_LINKS` in your config to `true` for a global change. + GROUP_LINKS: true, + + // Number of maximum redirects to follow before aborting the page + // request with `redirect loop` error. + MAX_REDIRECTS: 4, + + SKIP_OEMBED_RE_LIST: [ + // /^https?:\/\/yourdomain\.com\//, + ], + + /* + // Used to pass parameters to the generate functions when creating HTML elements + // disableSizeWrapper: Don't wrap element (iframe, video, etc) in a positioned div + GENERATE_LINK_PARAMS: { + disableSizeWrapper: true + }, + */ + + port: 8061, //can be overridden by PORT env var + host: '0.0.0.0', // Dockers beware. See https://github.com/itteco/iframely/issues/132#issuecomment-242991246 + //can be overridden by HOST env var + + // Optional SSL cert, if you serve under HTTPS. + /* + ssl: { + key: ssl_key, + cert: ssl_cert, + port: 443 + }, + */ + + /* + Supported cache engines: + - no-cache - no caching will be used. + - node-cache - good for debug, node memory will be used (https://github.com/tcs-de/nodecache). + - redis - https://github.com/mranney/node_redis. + - memcached - https://github.com/3rd-Eden/node-memcached + */ + CACHE_ENGINE: 'node-cache', + CACHE_TTL: 0, // In seconds. + // 0 = 'never expire' for memcached & node-cache to let cache engine decide itself when to evict the record + // 0 = 'no cache' for redis. Use high enough (e.g. 365*24*60*60*1000) ttl for similar 'never expire' approach instead + + /* + // Redis mode (cluster or standard) + REDIS_MODE: 'standard', + */ + + /* + // Redis cache options. + REDIS_OPTIONS: { + socket: { + host: '127.0.0.1', + port: 6379 + } + }, + */ + + /* + // Redis cluster options. + REDIS_CLUSTER_OPTIONS: { + servers: [ + { + host: '10.0.0.10', + port: 6379 + }, + // ... + ], + }, + */ + + /* + // Memcached options. See https://github.com/3rd-Eden/node-memcached#server-locations + MEMCACHED_OPTIONS: { + locations: "127.0.0.1:11211" + } + */ + + /* + // Access-Control-Allow-Origin list. + allowedOrigins: [ + "*", + "http://another_domain.com" + ], + */ + + /* + // Uncomment to enable plugin testing framework. + tests: { + mongodb: 'mongodb://localhost:27017/iframely-tests', + single_test_timeout: 10 * 1000, + plugin_test_period: 2 * 60 * 60 * 1000, + relaunch_script_period: 5 * 60 * 1000 + }, + */ + + // If there's no response from remote server, the timeout will occur after + RESPONSE_TIMEOUT: 5 * 1000, //ms + + // Customize API calls to oembed endpoints. + // Must have: please add your `access_token` for Facebook and Instagram API calls + ADD_OEMBED_PARAMS: [{ + + re: [ // Endpoint's URL regexp array. + /^https:\/\/graph\.facebook\.com\/v[0-9\.]+\/instagram_oembed/i + ], + params: { // Custom query-string params object. + + // TODO: get your access Insagtam token as described + // on https://developers.facebook.com/docs/instagram/oembed/ + access_token: '', // The simplest way is + // to use `{app-id}|{app secret}` as access token + + // Add any other optional params + hidecaption: true + } + }, { + re: [/^https:\/\/graph\.facebook\.com\/v[0-9\.]+\/oembed_page/i], + params: { + // TODO: get your access token as described + // on https://developers.facebook.com/docs/plugins/oembed + access_token: '', // The simplest way is + // to use `{app-id}|{app secret}` as access token + + // Add any other optional params + show_posts: 0, + show_facepile: 0, + maxwidth: 600 + } + }, { + // match i=user or i=moment or i=timeline to configure these types invidually + // see params spec at https://dev.twitter.com/web/embedded-timelines/oembed + re: [/^https?:\/\/publish\.twitter\.com\/oembed\?i=user/i], + params: { + limit: 1, + maxwidth: 600 + } + }, { + // Facebook https://developers.facebook.com/docs/plugins/oembed/ + re: [/^https:\/\/graph\.facebook\.com\/v[0-9\.]+\/oembed_/i], + params: { + // TODO: get your access token as described + // on https://developers.facebook.com/docs/plugins/oembed + access_token: '', // The simplest way is + // to use `{app-id}|{app secret}` as access token + + // Add any other optional params, like skip script tag and fb-root div + // omitscript: true + } + }], + + /* Configure use of HTTP proxies, headers as needed. + You don't have to specify all options per regex - just what you need to override + */ + /* + PROXY: [{ + re: [/^https?:\/\/www\.domain\.com/], + + // Either `proxy`, or `proxy_url`, or none. + proxy: true, // Will fetch URL via echo service configured as PROXY_URL. See below. + // proxy_url: 'http://1.2.3.4:8080?url={url}', // Will fetch URL via this exact echo service, see below. + + user_agent: 'CHANGE YOUR AGENT', + headers: { + // HTTP headers + // Overrides previous params if overlapped. + }, + cache_ttl: 3600 // in seconds, cache response for 1 hour. + }], + + // Proxy now requires an echo service endpoint. + // See #354 and example code at + // https://gist.github.com/nleush/7916ee89f7b8d6f0cd478d7335702139 + PROXY_URL: 'http://1.2.3.4:8080?url={url}', // Iframely will add `?url=...` to this endpoint + */ + + // Customize API calls to 3rd parties. At the very least - configure required keys. + // For available provider options - please see the code of its domain plugin. + providerOptions: { + locale: "en_US", // ISO 639-1 two-letter language code, e.g. en_CA or fr_CH. + // Will be added as highest priotity in accept-language header with each request. + // Plus is used in FB, YouTube and perhaps other plugins + "twitter": { + "max-width": 550, + "min-width": 250, + hide_media: false, + hide_thread: false, + omit_script: false, + center: false, + // dnt: true, + cache_ttl: 100 * 365 * 24 * 3600 // 100 Years. + }, + readability: { + enabled: false + // allowPTagDescription: true // to enable description fallback to first paragraph + }, + images: { + loadSize: false, // if true, will try an load first bytes of all images to get/confirm the sizes + checkFavicon: false // if true, will verify all favicons + }, + tumblr: { + consumer_key: "INSERT YOUR VALUE" + // media_only: true // disables status embeds for images and videos - will return plain media + }, + google: { + // https://developers.google.com/maps/documentation/embed/guide#api_key + maps_key: "INSERT YOUR VALUE" + }, + + /* + // Optional Camo Proxy to wrap all images: https://github.com/atmos/camo + camoProxy: { + camo_proxy_key: "INSERT YOUR VALUE", + camo_proxy_host: "INSERT YOUR VALUE" + // ssl_only: true // will only proxy non-ssl images + }, + */ + + // List of query parameters to add to YouTube and Vimeo frames + // Start it with leading "?". Or omit alltogether for default values + // API key is optional, youtube will work without it too. + // It is probably the same API key you use for Google Maps. + youtube: { + // api_key: "INSERT YOUR VALUE", + // parts: [ "snippet", "player" ], // list of fields you want to use in the request, in most cases you only need those two + get_params: "?rel=0&showinfo=1", // https://developers.google.com/youtube/player_parameters, + fix_shorts_in_eu: true // Avoid consent redirect for EU servers + }, + vimeo: { + get_params: "?byline=0&badge=0" // https://developer.vimeo.com/player/embedding + }, + soundcloud: { + old_player: true // enables classic player + }, + giphy: { + media_only: true // disables branded player for gifs and returns just the image + }, + bandcamp: { + get_params: '/size=large/bgcol=333333/linkcol=ffffff/artwork=small/transparent=true/', + media: { + album: { + height: 472, + 'max-width': 700 + }, + track: { + height: 120, + 'max-width': 700 + } + } + }, + // Docs: https://dev.twitch.tv/docs/embed/video-and-clips + /* + twitch: { + parent: 'jsbin.com, null.jsbin.com, localhost' + }, + */ + }, + + // WHITELIST_WILDCARD, if present, will be added to whitelist as record for top level domain: "*" + // with it, you can define what parsers do when they run accross unknown publisher. + // If absent or empty, all generic media parsers will be disabled except for known domains + // More about format: https://iframely.com/docs/qa-format + + /* + WHITELIST_WILDCARD: { + "twitter": { + "player": "allow", + "photo": "deny" + }, + "oembed": { + "video": "allow", + "photo": "allow", + "rich": "deny", + "link": "deny" + }, + "og": { + "video": ["allow", "ssl", "responsive"] + }, + "iframely": { + "survey": "allow", + "reader": "allow", + "player": "allow", + "image": "allow" + }, + "html-meta": { + "video": ["allow", "responsive"], + "promo": "allow" + } + } + */ + + // The list of regexs to be ignored. Iframely will return 417 + // At minimum, keep your localhosts ignored to avoid SSRF + IGNORE_DOMAINS_RE: [ + /^https?:\/\/127\.0\.0\.1/i, + /^https?:\/\/localhost/i, + /^https?:\/\/[^\/]+:\d+\/?/, // Blocks port-scan via DNS pointing to 127.0.0.1 + + // And this is AWS metadata service + // https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html + /^https?:\/\/169\.254\.169\.254/ + ], + + // Endpoint for prerender service, if you need it. Used to parse React apps. Very slow. + // Tested with https://github.com/prerender/prerender + // PRERENDER_URL: "https://domain/render?url=" +}; diff --git a/apps/iframely/2.4.3/data.yml b/apps/iframely/2.4.3/data.yml new file mode 100644 index 000000000..4fbe71b7e --- /dev/null +++ b/apps/iframely/2.4.3/data.yml @@ -0,0 +1,24 @@ +additionalProperties: + formFields: + - default: "/home/iframely" + edit: true + envKey: IFRAMELY_ROOT_PATH + labelZh: 数据持久化路径 + labelEn: Data persistence path + required: true + type: text + - default: 8061 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelZh: WebUI 端口 + labelEn: WebUI port + required: true + rule: paramPort + type: number + - default: "" + edit: true + envKey: HTTP_PROXY + labelZh: 网络代理 + labelEn: Network Proxy + required: false + type: text diff --git a/apps/iframely/2.4.3/docker-compose.yml b/apps/iframely/2.4.3/docker-compose.yml new file mode 100644 index 000000000..2c21b7005 --- /dev/null +++ b/apps/iframely/2.4.3/docker-compose.yml @@ -0,0 +1,25 @@ +version: "3.8" + +networks: + 1panel-network: + external: true + +services: + iframely: + image: qyg2297248353/iframely:v2.4.3 + container_name: ${CONTAINER_NAME} + labels: + createdBy: "Apps" + restart: always + networks: + - 1panel-network + ports: + - ${PANEL_APP_PORT_HTTP}:8080 + env_file: + - /etc/1panel/envs/global.env + - ${ENV_FILE:-/etc/1panel/envs/default.env} + volumes: + - ./config/config.local.js:/iframely/config.local.js + environment: + - NODE_ENV=production + - HTTPS_PROXY=${HTTP_PROXY} diff --git a/apps/iframely/2.4.3/scripts/init.sh b/apps/iframely/2.4.3/scripts/init.sh new file mode 100644 index 000000000..77b849120 --- /dev/null +++ b/apps/iframely/2.4.3/scripts/init.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +if [ -f .env ]; then + source .env + + # setup-1 add default values + CURRENT_DIR=$(pwd) + sed -i '/^ENV_FILE=/d' .env + echo "ENV_FILE=${CURRENT_DIR}/.env" >> .env + + echo "Check Finish." + +else + echo "Error: .env file not found." +fi diff --git a/apps/iframely/2.4.3/scripts/uninstall.sh b/apps/iframely/2.4.3/scripts/uninstall.sh new file mode 100644 index 000000000..c86c4fbca --- /dev/null +++ b/apps/iframely/2.4.3/scripts/uninstall.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +if [ -f .env ]; then + source .env + + echo "Check Finish." + +else + echo "Error: .env file not found." +fi diff --git a/apps/iframely/2.4.3/scripts/upgrade.sh b/apps/iframely/2.4.3/scripts/upgrade.sh new file mode 100644 index 000000000..77b849120 --- /dev/null +++ b/apps/iframely/2.4.3/scripts/upgrade.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +if [ -f .env ]; then + source .env + + # setup-1 add default values + CURRENT_DIR=$(pwd) + sed -i '/^ENV_FILE=/d' .env + echo "ENV_FILE=${CURRENT_DIR}/.env" >> .env + + echo "Check Finish." + +else + echo "Error: .env file not found." +fi diff --git a/apps/iframely/README.md b/apps/iframely/README.md new file mode 100644 index 000000000..09f0f7c5c --- /dev/null +++ b/apps/iframely/README.md @@ -0,0 +1,46 @@ +# Iframely + +当今互联网的富媒体平台 + + + +iframely 是针对所有各种富媒体嵌入和 URL 数据的统一交付服务。 + +![](https://img.shields.io/badge/%E6%96%B0%E7%96%86%E8%90%8C%E6%A3%AE%E8%BD%AF%E4%BB%B6%E5%BC%80%E5%8F%91%E5%B7%A5%E4%BD%9C%E5%AE%A4-%E6%8F%90%E4%BE%9B%E6%8A%80%E6%9C%AF%E6%94%AF%E6%8C%81-blue) + +## 特点 + ++ 最佳富媒体 + +iframely 了解来自 1900 多家出版商的富媒体,并且还在不断增加。视频、音频、应用程序、照片和图像、幻灯片、播放列表、播客、地图、3D、表格、文档、谜题、测验、图表和信息图表。您会自动获得所有新的发布商。 + +## API 端点 + ++ 请求示例 + +```http request +${domain}/api/oembed?url={URL}&api_key={API_KEY} +``` + ++ 响应示例 + +```json +{ + "url": "https://vimeo.com/141567420", + "type": "video", + "version": "1.0", + "title": "Input/Output", + "description": "A new short from Terri Timely and Park Pictures", + "author": "Terri Timely", + "author_url": "https://vimeo.com/user1946955", + "provider_name": "Vimeo", + "thumbnail_url": "https://i.vimeocdn.com/…5aebf015a6472-d_295x166", + "thumbnail_width": 295, + "thumbnail_height": 166, + "html": "
" +} +``` + +--- + +![Ms Studio](https://file.lifebus.top/imgs/ms_blank_001.png) diff --git a/apps/iframely/data.yml b/apps/iframely/data.yml new file mode 100644 index 000000000..2bc8aec69 --- /dev/null +++ b/apps/iframely/data.yml @@ -0,0 +1,18 @@ +name: Iframely +title: 响应式 Web 嵌入和 URL 元的 Iframely API +description: 响应式 Web 嵌入和 URL 元的 Iframely API +additionalProperties: + key: iframely + name: Iframely + tags: + - Tool + - Middleware + - Local + shortDescZh: 响应式 Web 嵌入和 URL 元的 Iframely API + shortDescEn: Responsive Web Embeds and URL Meta with Iframely API + type: tool + crossVersionUpdate: true + limit: 0 + website: https://iframely.com/ + github: https://github.com/itteco/iframely/ + document: https://iframely.com/docs/ diff --git a/apps/iframely/logo.png b/apps/iframely/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..b392f442a67eb46ffd3cb9bc8f06def4268fbed4 GIT binary patch literal 14374 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 D6U+6+ literal 0 HcmV?d00001