├── .gitignore ├── LICENSE ├── README.md ├── base ├── __init__.py └── util.py ├── etc ├── __init__.py ├── config.py.sample ├── const.py ├── nginx │ └── 163.gs └── supervisor │ └── 163.gs.conf ├── log └── placeholder ├── main.py ├── model ├── __init__.py └── connection.py.sample ├── requirements.txt └── templates └── short.html /.gitignore: -------------------------------------------------------------------------------- 1 | etc/config.py 2 | .env/ 3 | start.sh 4 | log.log 5 | webscan_360_cn.html 6 | google7a32e07f62c143af.html 7 | reload.sh 8 | log/ 9 | model/connection.py 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2014 EverET.org. https://github.com/cedricporter/url-shorten 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 163.gs url shorten service 2 | 3 | This is the source code of [http://163.gs](http://163.gs). 4 | 5 | 163.gs is written by Python with Tornado. 6 | 7 | ## Generate shorten url interface 8 | 9 | ``` 10 | curl -d "url=http://EverET.org" http://163.gs/short/ 11 | ``` 12 | 13 | Return short url if success. You can check whether the response is started with `http:` to determine if it is successful. 14 | 15 | If fail, it returns the error message. 16 | 17 | ## Install 18 | Copy `etc/config.py.sample` to `etc/config.py` and modify it if necessary. 19 | 20 | ## Dependency 21 | Run `pip install -r requirements.txt` to install dependency. We advise you to install them in virtualenv. 22 | 23 | ``` 24 | $ virtualenv .env 25 | $ source .env/bin/activate 26 | $ pip install -r requirements.txt 27 | ``` 28 | 29 | ## Simple deploy 30 | And you can run execute `./main.py` to run it. 31 | 32 | ## By supervisor 33 | Copy `etc/supervisor/163.gs.conf` to your supervisord config folder, and modify it accordingly. Then you can run it with supervisord. 34 | 35 | It will open two processes that listen on 8850, 8851, you can use `etc/nginx/163.gs` as your nginx config to proxy it. 36 | 37 | -------------------------------------------------------------------------------- /base/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /base/util.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # By Hua Liang[Stupid ET] 4 | 5 | import re 6 | import string 7 | import random 8 | from etc import config 9 | 10 | 11 | regex = re.compile( 12 | r'^(?:http|ftp)s?://' # http:// or https:// 13 | r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' #domain... 14 | r'localhost|' #localhost... 15 | r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip 16 | r'(?::\d+)?' # optional port 17 | r'(?:/?|[/?]\S+)$', re.IGNORECASE) 18 | 19 | 20 | def validate_url(url): 21 | return bool(regex.match(url)) 22 | 23 | 24 | def gen_short_id(length=config.SHORT_ID_LENGTH): 25 | return "".join(random.sample(string.digits + string.ascii_letters, length)) 26 | 27 | 28 | def gen_cache_key(*args): 29 | return ":".join(str(item) for item in args) 30 | 31 | -------------------------------------------------------------------------------- /etc/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /etc/config.py.sample: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | PORT = 8865 4 | DEBUG = False 5 | 6 | MAIN_PAGE_REDIRECT = "https://github.com/cedricporter/url-shorten" 7 | SITE_URL = "http://163.gs/" 8 | SHORT_ID_LENGTH = 5 9 | 10 | 11 | -------------------------------------------------------------------------------- /etc/const.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | class CACHE_KEY_PREFIX(object): 4 | SHORT_ID = "id" # short_id to url 5 | REVERSE_URL = "url" # url to short_id 6 | 7 | 8 | -------------------------------------------------------------------------------- /etc/nginx/163.gs: -------------------------------------------------------------------------------- 1 | upstream 163gs_upstream { 2 | server 127.0.0.1:8850; 3 | server 127.0.0.1:8851; 4 | } 5 | 6 | server { 7 | server_name 163.gs; 8 | 9 | access_log /var/log/nginx/163.gs.access.log; 10 | error_log /var/log/nginx/163.gs.error.log; 11 | 12 | location / { 13 | proxy_pass http://163gs_upstream; 14 | proxy_pass_header Server; 15 | proxy_set_header Host $http_host; 16 | proxy_set_header X-Real-IP $remote_addr; 17 | proxy_set_header X-Scheme $scheme; 18 | } 19 | } 20 | 21 | server { 22 | server_name www.163.gs; 23 | 24 | return 301 $scheme://163.gs$request_uri; 25 | } 26 | -------------------------------------------------------------------------------- /etc/supervisor/163.gs.conf: -------------------------------------------------------------------------------- 1 | [program:163gs] 2 | numprocs = 2 3 | numprocs_start = 8850 4 | user = projects 5 | process_name = 163gs-%(process_num)s 6 | directory = /home/projects/163.gs/ 7 | command = /home/projects/163.gs/env/bin/python /home/projects/163.gs/main.py --port=%(process_num)s 8 | autorestart = true 9 | redirect_stderr = true 10 | stdout_logfile = /var/log/supervisor/163gs.log 11 | stderr_logfile = /var/log/supervisor/163gs-error.log -------------------------------------------------------------------------------- /log/placeholder: -------------------------------------------------------------------------------- 1 | ET 2 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # By Hua Liang[Stupid ET] 4 | 5 | import urlparse 6 | 7 | import tornado.options 8 | from tornado.options import define, options 9 | from tornado.web import HTTPError 10 | import tornado.gen 11 | import tornado.httpserver 12 | import tornado.options 13 | import tornado.web 14 | import tornadoredis 15 | 16 | import model 17 | from model.connection import CONNECTION_POOL 18 | 19 | from etc import config, const 20 | from base import util 21 | 22 | define("port", default=config.PORT, help="run on the given port", type=int) 23 | 24 | 25 | class MainHandler(tornado.web.RequestHandler): 26 | def get(self): 27 | self.render("templates/short.html") 28 | 29 | 30 | class ExpandUrlHandler(tornado.web.RequestHandler): 31 | @tornado.web.asynchronous 32 | @tornado.gen.engine 33 | def get(self, short_id): 34 | c = tornadoredis.Client(connection_pool=CONNECTION_POOL) 35 | key = util.gen_cache_key(const.CACHE_KEY_PREFIX.SHORT_ID, short_id) 36 | url = yield tornado.gen.Task(c.get, key) 37 | if url: 38 | self.redirect(url, permanent=True) 39 | else: 40 | raise HTTPError(404) 41 | 42 | 43 | class ShortenUrlHandler(tornado.web.RequestHandler): 44 | @tornado.web.asynchronous 45 | @tornado.gen.engine 46 | def post(self): 47 | c = tornadoredis.Client(connection_pool=CONNECTION_POOL) 48 | url = self.get_argument("url") 49 | if not util.validate_url(url): 50 | self.write("not a valid url") 51 | self.finish() 52 | return 53 | 54 | key = util.gen_cache_key(const.CACHE_KEY_PREFIX.REVERSE_URL, url) 55 | short_id = redis_short_id = yield tornado.gen.Task(c.get, key) 56 | 57 | while not redis_short_id: 58 | short_id = util.gen_short_id() 59 | key = util.gen_cache_key(const.CACHE_KEY_PREFIX.SHORT_ID, short_id) 60 | ret = yield tornado.gen.Task(c.get, key) 61 | if ret: 62 | continue 63 | 64 | yield tornado.gen.Task(c.set, key, url) 65 | key = util.gen_cache_key(const.CACHE_KEY_PREFIX.REVERSE_URL, url) 66 | yield tornado.gen.Task(c.set, key, short_id) 67 | break 68 | 69 | self.write(urlparse.urljoin(config.SITE_URL, short_id)) 70 | self.finish() 71 | 72 | 73 | application = tornado.web.Application([ 74 | (r"/", MainHandler), 75 | (r"/short/?", ShortenUrlHandler), 76 | (r"/([a-zA-Z0-9]{%s})/?" % config.SHORT_ID_LENGTH, ExpandUrlHandler), 77 | ], debug=config.DEBUG) 78 | 79 | 80 | if __name__ == "__main__": 81 | tornado.options.parse_command_line() 82 | http_server = tornado.httpserver.HTTPServer(application, xheaders=True) 83 | http_server.listen(options.port) 84 | tornado.ioloop.IOLoop.instance().start() 85 | -------------------------------------------------------------------------------- /model/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /model/connection.py.sample: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # By Hua Liang[Stupid ET] 4 | 5 | import tornadoredis 6 | 7 | 8 | MAX_CONNECTIONS = 10 9 | UNIX_SOCKET_PATH = "/var/run/redis/redis-163gs.sock" 10 | 11 | CONNECTION_POOL = tornadoredis.ConnectionPool(max_connections=MAX_CONNECTIONS, 12 | unix_socket_path=UNIX_SOCKET_PATH, 13 | wait_for_available=True) 14 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | tornado==3.2.1 2 | tornado-redis==2.4.17 3 | -------------------------------------------------------------------------------- /templates/short.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 163.gs短网址服务 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 62 | 63 | 64 | 65 |
66 |
67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 |
76 |

Powered by Stupid ET. 接口文档

77 |
78 |
79 | 80 | 81 | 82 |
83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 116 | 117 | 126 | 127 | 128 | 129 | 130 | 131 | --------------------------------------------------------------------------------