├── .gitignore ├── LICENSE ├── README.md ├── _config.yml ├── index.js ├── package.json └── style.css /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /dist 3 | **/*.rs.bk 4 | Cargo.lock 5 | bin/ 6 | pkg/ 7 | wasm-pack.log 8 | worker/ 9 | node_modules/ 10 | .cargo-ok 11 | playground.html -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 sleepwood 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CF-Worker-Dir 2 | 3 | CF-Worker-Dir is a cloud function program suitable for Cloudflare Worker platform. You can use it to build your own navigation page within one minute. CF-Worker-Dir provides a lot of custom configurations, and it can also reserve an interface to help you sell your own domain name. If your domain name has not yet built a website, it is better to use CF-Worker-Dir to make your domain name no longer wasted.😉 4 | 5 | 🎉[Demo1](http://sites.51sec.org/) 6 | 7 | 🎉[Demo2](http://sites.itprosec.com/) 8 | 9 | ## Features 10 | - Categories / Sections 11 | - Auto-fetch favicon 12 | - Selling ads 13 | - Hitokoto - one sentence note 14 | - No server / service required 15 | - Fast and free using Cloudflare workers 16 | - Configuration / Data in one text file 17 | 18 |
19 | 📷Screenshot 20 | 21 |
22 | 23 | 24 | ## Installation 25 | ### Quick installation Steps 26 | 1. from [Cloudflare Worker](https://workers.cloudflare.com/) management page, create a new **Worker** 。 27 | 2. Paste the code in `index.js` to the left side of Worker's edit page 28 | 3. Modify `config` setction's content based on your requirement 29 | 30 | 31 | ### Advanced steps 32 | > How to bind it with your own subdomain 33 | 1. After completed quick installation steps,go back to your Cloudflare domain management console 34 | 2. Click `Workers` to enter Workers management page 35 | 3. Click `Add route` to create a new route 36 | 4. `Route` : you can enter your own subdomain for your route. If you would like to use your root domain, you can directly enter it,`Worker`: choose the Worker created from previous step to bind with your own subdomain. 37 | > `Route`'s subdomain **must has a corresponding A record created to be able to be resolved**. If it has been to creaet a A record to map to an IP address, you can enter 8.8.8.8 as IP address when createing it:) 38 | 39 | ## System Configuration 40 | 41 | CF-Worker-Dir allows user to customize configuraiton. Here is explaination about those customization configuration: 42 | ``` 43 | /** 44 | * Customized Sites Definition 45 | * Original Github address : https://github.com/sleepwood/CF-Worker-Dir/ 46 | * https://github.com/51sec/CF-Worker-Dir/blob/master/index.js 47 | */ 48 | const config = { 49 | title: "My Bookmarks", //write your website title 50 | subtitle: "Cloudflare Workers Dir", //write your website subtitle 51 | logo_icon: "sitemap", //select your logo by semantic-ui icon (you can get more msg in:https://semantic-ui.com/elements/icon.html) 52 | hitokoto: false, //use hitokoto or not 53 | search:false, //enable search function 54 | search_engine:[ //choose search engine which you use 55 | { 56 | name:"Google", 57 | template:"https://www.google.com/search?q=$s" 58 | }, 59 | { 60 | name:"Baidu", 61 | template:"https://www.baidu.com/s?wd=$s" 62 | }, { 63 | name:"Bing", 64 | template:"https://www.bing.com/search?q=$s" 65 | }, 66 | { 67 | name:"Sogou", 68 | template:"https://www.sogou.com/web?query=$s" 69 | } 70 | ], 71 | selling_ads: false, //Selling your domain or not.(turning on may be helpful for selling this domain by showing some ads.) 72 | sell_info:{ 73 | domain:"51sec.org", 74 | price:5000, //domain price 75 | mon_unit:"usd sign", //monetary unit 76 | contact:[ //how to contact you 77 | { 78 | type:"envelope", //contact type ("weixin","qq","telegram plane","envelope" or "phone") 79 | content:"admin@51sec.org" 80 | } 81 | ] 82 | }, 83 | lists: [ //Url list 84 | { 85 | name:"CyberSecurity", 86 | icon:"shield", 87 | list:[ 88 | { 89 | url:"https://www.51sec.org/", 90 | name:"Info Security Memo", 91 | desc:"Information Security Memo" 92 | }, 93 | { 94 | url:"https://Blog.51sec.org", 95 | name:"Cyber Security Blog", 96 | desc:"Cyber Security Memo" 97 | }, 98 | { 99 | url:"https://itprosec.com/", 100 | name:"ITProSec", 101 | desc:"IT Professional Security" 102 | }, 103 | { 104 | url:"https://opc2portainer.51sec.org", 105 | name:"Portainer", 106 | desc:"Portainer" 107 | }, 108 | ] 109 | }, 110 | 111 | { 112 | name:"InfoSecurity", 113 | icon:"id-badge", 114 | list:[ 115 | { 116 | url:"https://51sec.loggly.com/", 117 | name:"Loggly", 118 | desc:"Loggly" 119 | }, 120 | { 121 | url:"https://proxy.51sec.org/", 122 | name:"Proxy", 123 | desc:"Proxy" 124 | }, 125 | { 126 | url:"https://gd.51sec.workers.dev/", 127 | name:"Google Drive", 128 | desc:"Google Drive" 129 | }, 130 | { 131 | url:"https://od.51sec.org", 132 | name:"One Drive", 133 | desc:"One Drive" 134 | }, 135 | ] 136 | }, 137 | 138 | { 139 | name:"51Sec App", 140 | icon:"id-badge", 141 | list:[ 142 | { 143 | url:"https://51sec.org/mail", 144 | name:"51Sec Webmail", 145 | desc:"51Sec Webmail" 146 | }, 147 | { 148 | url:"https://sec.myxwiki.org/", 149 | name:"51Sec Wiki", 150 | desc:"51Sec Wiki" 151 | }, 152 | { 153 | url:"https://calendly.com/51sec/", 154 | name:"51Sec Calendar", 155 | desc:"Google Drive" 156 | }, 157 | { 158 | url:"https://myod.51sec.eu.org", 159 | name:"51Sec EU OneDrive", 160 | desc:"51Sec EU OneDrive" 161 | }, 162 | ] 163 | }, 164 | 165 | { 166 | name:"Technologies", 167 | icon:"code", 168 | list:[ 169 | { 170 | url:"https://oschina.net/", 171 | name:"开源中国", 172 | desc:"程序员集散地" 173 | }, 174 | { 175 | url:"https://v2ex.com", 176 | name:"V2EX", 177 | desc:"程序员集散地" 178 | }, 179 | { 180 | url:"https://csdn.net/", 181 | name:"CSDN技术社区", 182 | desc:"程序员集散地" 183 | }, 184 | { 185 | url:"https://github.com/", 186 | name:"Github", 187 | desc:"程序员集散地" 188 | }, 189 | ] 190 | }, 191 | { 192 | name:"Learning", 193 | icon:"graduation cap", 194 | list:[ 195 | { 196 | url:"https://w3school.com.cn/", 197 | name:"W3school在线教程", 198 | desc:"程序员集散地" 199 | }, 200 | { 201 | url:"https://runoob.com/", 202 | name:"菜鸟教程", 203 | desc:"程序员集散地" 204 | }, 205 | { 206 | url:"https://segmentfault.com/", 207 | name:"思否社区", 208 | desc:"程序员集散地" 209 | }, 210 | { 211 | url:"https://jianshu.com/", 212 | name:"简书", 213 | desc:"程序员集散地" 214 | }, 215 | ] 216 | } 217 | ] 218 | } 219 | 220 | ``` 221 | 222 | 223 | ## Licence 224 | 225 | MIT 226 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-leap-day -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Customized Sites Definition 3 | * Github address : https://github.com/sleepwood/CF-Worker-Dir/ 4 | * https://github.com/51sec/CF-Worker-Dir/blob/master/index.js 5 | */ 6 | const config = { 7 | title: "51Sec Sites Dashboard", //write your website title 8 | subtitle: "A Web Sites Dashboard Based on Cloudflare Workers", //write your website subtitle 9 | logo_icon: "sitemap", //select your logo by semantic-ui icon (you can get more msg in:https://semantic-ui.com/elements/icon.html) 10 | hitokoto: false, //use hitokoto or not 11 | search:true, //enable search function 12 | search_engine:[ //choose search engine which you use 13 | { 14 | name:"Google", 15 | template:"https://www.google.com/search?q=$s" 16 | }, 17 | { 18 | name:"Baidu", 19 | template:"https://www.baidu.com/s?wd=$s" 20 | }, { 21 | name:"Bing", 22 | template:"https://www.bing.com/search?q=$s" 23 | }, 24 | { 25 | name:"Sogou", 26 | template:"https://www.sogou.com/web?query=$s" 27 | } 28 | ], 29 | selling_ads: false, //Selling your domain or not.(turning on may be helpful for selling this domain by showing some ads.) 30 | sell_info:{ 31 | domain:"51sec.org", 32 | price:5000, //domain price 33 | mon_unit:"usd sign", //monetary unit 34 | contact:[ //how to contact you 35 | { 36 | type:"envelope", //contact type ("weixin","qq","telegram plane","envelope" or "phone") 37 | content:"admin@51sec.org" 38 | } 39 | ] 40 | }, 41 | lists: [ //Url list 42 | { 43 | name:"CyberSecurity", 44 | icon:"shield", 45 | list:[ 46 | { 47 | url:"https://www.51sec.org/", 48 | name:"Info Security Memo", 49 | desc:"Information Security Memo" 50 | }, 51 | { 52 | url:"https://Blog.51sec.org", 53 | name:"Cyber Security Blog", 54 | desc:"Cyber Security Memo" 55 | }, 56 | { 57 | url:"https://itprosec.com/", 58 | name:"ITProSec", 59 | desc:"IT Professional Security" 60 | }, 61 | { 62 | url:"https://opc2portainer.51sec.org", 63 | name:"Portainer", 64 | desc:"Portainer" 65 | }, 66 | ] 67 | }, 68 | 69 | { 70 | name:"InfoSecurity", 71 | icon:"id-badge", 72 | list:[ 73 | { 74 | url:"https://51sec.loggly.com/", 75 | name:"Loggly", 76 | desc:"Loggly" 77 | }, 78 | { 79 | url:"https://proxy.itprosec.eu.org/", 80 | name:"Proxy", 81 | desc:"Proxy" 82 | }, 83 | { 84 | url:"https://gd.51sec.org", 85 | name:"Google Drive", 86 | desc:"Google Drive" 87 | }, 88 | { 89 | url:"https://od.51sec.org", 90 | name:"One Drive", 91 | desc:"One Drive" 92 | }, 93 | ] 94 | }, 95 | 96 | { 97 | name:"51Sec App", 98 | icon:"id-badge", 99 | list:[ 100 | { 101 | url:"https://51sec.org/mail", 102 | name:"51Sec Webmail", 103 | desc:"51Sec Webmail" 104 | }, 105 | { 106 | url:"https://sec.myxwiki.org/", 107 | name:"51Sec Wiki", 108 | desc:"51Sec Wiki" 109 | }, 110 | { 111 | url:"https://calendly.com/51sec/", 112 | name:"51Sec Calendar", 113 | desc:"Book a 15 mins meeting with Netsec" 114 | }, 115 | { 116 | url:"https://myod.51sec.eu.org", 117 | name:"51Sec EU OneDrive", 118 | desc:"51Sec EU OneDrive" 119 | }, 120 | { 121 | url:"https://ip.51sec.org/api", 122 | name:"Your Public IP", 123 | desc:"Show Your Public IP Address" 124 | }, 125 | { 126 | url:"https://nav.51sec.org", 127 | name:"51Sec Navigation", 128 | desc:"Bookmarks for all kinds of things, Software, Website, tools, Tutorials" 129 | }, 130 | { 131 | url:"https://go.51sec.org", 132 | name:"URL Shortener", 133 | desc:"Shorten your long URL" 134 | }, 135 | ] 136 | }, 137 | 138 | { 139 | name:"Technologies", 140 | icon:"code", 141 | list:[ 142 | { 143 | url:"https://oschina.net/", 144 | name:"开源中国", 145 | desc:"程序员集散地" 146 | }, 147 | { 148 | url:"https://v2ex.com", 149 | name:"V2EX", 150 | desc:"程序员集散地" 151 | }, 152 | { 153 | url:"https://csdn.net/", 154 | name:"CSDN技术社区", 155 | desc:"程序员集散地" 156 | }, 157 | { 158 | url:"https://github.com/", 159 | name:"Github", 160 | desc:"程序员集散地" 161 | }, 162 | ] 163 | }, 164 | { 165 | name:"Learning", 166 | icon:"graduation cap", 167 | list:[ 168 | { 169 | url:"https://w3school.com.cn/", 170 | name:"W3school在线教程", 171 | desc:"程序员集散地" 172 | }, 173 | { 174 | url:"https://runoob.com/", 175 | name:"菜鸟教程", 176 | desc:"程序员集散地" 177 | }, 178 | { 179 | url:"https://segmentfault.com/", 180 | name:"思否社区", 181 | desc:"程序员集散地" 182 | }, 183 | { 184 | url:"https://jianshu.com/", 185 | name:"简书", 186 | desc:"程序员集散地" 187 | }, 188 | ] 189 | } 190 | ] 191 | } 192 | const el = (tag, attrs, content) => `<${tag} ${attrs.join(" ")}>${content}`; 193 | 194 | async function handleRequest(request) { 195 | const init = { 196 | headers: { 197 | 'content-type': 'text/html;charset=UTF-8', 198 | }, 199 | } 200 | return new Response(renderHTML(renderIndex(),config.selling_ads? renderSeller() :null), init); 201 | } 202 | addEventListener('fetch', event => { 203 | return event.respondWith(handleRequest(event.request)) 204 | }) 205 | 206 | /*通过分析链接 实时获取favicon 207 | * @url 需要分析的Url地址 208 | */ 209 | function getFavicon(url){ 210 | if(url.match(/https{0,1}:\/\//)){ 211 | //return "https://ui-avatars.com/api/?bold=true&size=36&background=0D8ABC&color=fff&rounded=true&name=" + url.split('//')[1]; 212 | //return "http://icon.occ.hk/get.php?url=" + url; 213 | return "https://www.google.com/s2/favicons?sz=64&domain_url=" + url; 214 | }else{ 215 | //return "https://ui-avatars.com/api/?bold=true&size=36&background=0D8ABC&color=fff&rounded=true&name=" + url; 216 | //return "http://icon.occ.hk/get.php?url=http://" + url; 217 | return "https://www.google.com/s2/favicons?sz=64&domain_url=http://" + url; 218 | } 219 | } 220 | 221 | /** Render Functions 222 | * 渲染模块函数 223 | */ 224 | 225 | function renderIndex(){ 226 | const footer = el('footer',[],el('div',['class="footer"'],'Powered by' + el('a',['class="ui label"','href="https://github.com/51sec/cf-worker-dir"','target="_blank"'],el('i',['class="github icon"'],"") + 'Cf-Worker-Dir') + ' © Base on ' + el('a',['class="ui label"'],el('i',['class="balance scale icon"'],"") + 'MIT License'))); 227 | return renderHeader() + renderMain() + footer; 228 | } 229 | 230 | function renderHeader(){ 231 | const item = (template,name) => el('a',['class="item"',`data-url="${template}"`],name); 232 | 233 | var nav = el('div',['class="ui large secondary inverted menu"'],el('div',['class="item"'],el('p',['id="hitokoto"'],'All roads lead to Rome'))) 234 | var title = el('h1',['class="ui inverted header"'],el('i',[`class="${config.logo_icon} icon"`],"") + el('div',['class="content"'],config.title + el('div',['class="sub header"'],config.subtitle))); 235 | var menu = el('div',['id="sengine"','class="ui bottom attached tabular inverted secondary menu"'],el('div',['class="header item"'],' ') + config.search_engine.map((link,key) =>{ 236 | if(key == 0){ 237 | return el('a',['class="active item"',`data-url="${link.template}"`],link.name); 238 | }else{ 239 | return item(link.template,link.name); 240 | } 241 | }).join("")) 242 | var input = el('div',['class="ui left corner labeled right icon fluid large input"'],el('div',['class="ui left corner label"'],el('img',['id="search-fav"','class="left floated avatar ui image"','src="https://www.google.com/favicon.ico"'],"")) + el('input',['id="searchinput"','type="search"','placeholder="Type anything you want to search……"','autocomplete="off"'],"") + el('i',['class="inverted circular search link icon"'],"")); 243 | return el('header',[],el('div',['id="head"','class="ui inverted vertical masthead center aligned segment"'],(config.hitokoto ? el('div',['id="nav"','class="ui container"'],nav) : "") + el('div',['id="title"','class="ui text container"'],title + (config.search ? input + menu :"") + `${config.selling_ads ? '
Like this domain
' : ''}`))) 244 | } 245 | 246 | function renderMain() { 247 | var main = config.lists.map((item) => { 248 | const card = (url,name,desc)=> el('a',['class="card"',`href=${url}`,'target="_blank"'],el('div',['class="content"'],el('img',['class="left floated avatar ui image"',`src=${getFavicon(url)}`],"") + el('div',['class="header"'],name) + el('div',['class="meta"'],desc))); 249 | const divider = el('h4',['class="ui horizontal divider header"'],el('i',[`class="${item.icon} icon"`],"")+item.name); 250 | 251 | var content = el('div',['class="ui four stackable cards"'],item.list.map((link) =>{ 252 | return card(link.url,link.name,link.desc); 253 | }).join("")); 254 | 255 | return el('div',['class="ui basic segment"'],divider + content); 256 | }).join(""); 257 | 258 | return el('main',[],el('div',['class="ui container"'],main)); 259 | } 260 | 261 | function renderSeller() { 262 | const item = (type,content) => el('div',['class="item"'],el('i',[`class="${type} icon"`],"") + el('div',['class="content"'],content)); 263 | var title = el('h1',['class="ui yellow dividing header"'],el('i',['class="gem outline icon"'],"") + el('div',['class="content"'],config.sell_info.domain + ' Selling')); 264 | var action = el('div',['class="actions"'],el('div',['class="ui basic cancel inverted button"'],el('i',['class="reply icon"'],"") + 'Return')); 265 | 266 | var contact = config.sell_info.contact.map((list) => { 267 | return item(list.type,list.content); 268 | }).join(""); 269 | var column = el('div',['class="column"'],el('h3',['class="ui center aligned icon inverted header"'],el('i',['class="circular envelope open outline grey inverted icon"'],"") + 'Contact me') + el('div',['class="ui relaxed celled large list"'],contact)); 270 | var price = el('div',['class="column"'],el('div',['class="ui large yellow statistic"'],el('div',['class="value"'],el('i',[`class="${config.sell_info.mon_unit} icon"`],"") + config.sell_info.price))); 271 | var content = el('div',['class="content"'],el('div',['class="ui basic segment"'],el('div',['class="ui two column stackable center aligned grid"'],el('div',['class="ui inverted vertical divider"'],'Interested?') + el('div',['class="middle aligned row"'],price + column)))); 272 | 273 | return el('div',['id="seller"','class="ui basic modal"'],title + content + action); 274 | } 275 | 276 | function renderHTML(index,seller) { 277 | return ` 278 | 279 | 280 | 281 | 282 | 283 | ${config.title} - ${config.subtitle} 284 | 285 | 286 | 287 | 288 | 289 | 290 | ${index} 291 | ${config.selling_ads ? seller : ''} 292 | 293 | 315 | 316 | 317 | ` 318 | } 319 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "CF-Worker-Dir", 4 | "version": "0.1.1", 5 | "description": "A Free Dashboard Showing Your Website Collections", 6 | "main": "index.js", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1", 9 | "format": "prettier --write '**/*.{js,css,json,md}'" 10 | }, 11 | "author": "51sec.org", 12 | "license": "MIT", 13 | "devDependencies": { 14 | "prettier": "^1.18.2" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | body { 2 | width: -webkit-fill-available; 3 | height: 100%; 4 | display: flex; 5 | flex-direction: column; 6 | -webkit-flex-direction: column; 7 | -moz-flex-direction: column; 8 | -o-flex-direction: column; 9 | } 10 | 11 | header { 12 | text-align: center; 13 | flex-grow: 0; 14 | } 15 | 16 | #head { 17 | background-image: linear-gradient(to top, #537895 0%, #09203f 100%); 18 | } 19 | 20 | #nav .item { 21 | display: inline-block; 22 | width: -webkit-fill-available; 23 | } 24 | 25 | h1 { 26 | display: inline-block; 27 | } 28 | 29 | #title { 30 | padding: 5rem 0; 31 | } 32 | 33 | #menubtn { 34 | margin-top: 3rem; 35 | } 36 | 37 | main { 38 | flex-basis: auto; 39 | flex-grow: 1; 40 | padding: 2rem 0; 41 | background-image: radial-gradient(73% 147%, #EADFDF 59%, #ECE2DF 100%), radial-gradient(91% 146%, rgba(255,255,255,0.50) 47%, rgba(0,0,0,0.50) 100%); 42 | background-blend-mode: screen; 43 | } 44 | 45 | main .container{ 46 | height: 100%; 47 | display: flex !important; 48 | flex-direction: column; 49 | -webkit-flex-direction: column; 50 | -moz-flex-direction: column; 51 | -o-flex-direction: column; 52 | } 53 | 54 | main .segment { 55 | flex-grow: 1; 56 | margin: 0 !important; 57 | } 58 | 59 | .segment h4{ 60 | padding-bottom: .5rem !important; 61 | } 62 | 63 | footer { 64 | flex-grow: 0 65 | } 66 | 67 | .footer { 68 | padding: 1rem 0; 69 | text-align: center; 70 | background-color: #ddd; 71 | } 72 | 73 | .card { 74 | padding: .5rem; 75 | } 76 | 77 | #seller .list { 78 | padding: 0 5rem; 79 | } 80 | 81 | @media screen and (max-width: 479px) { 82 | p { 83 | width: -webkit-fill-available; 84 | text-align: center; 85 | line-height: 1.5rem; 86 | word-wrap: break-word; 87 | } 88 | } --------------------------------------------------------------------------------