mirror of
https://github.com/pirate/ArchiveBox.git
synced 2025-08-27 00:05:27 +02:00
Merge pull request #274 from pirate/v0.4.3
v0.4.3 (pre-release, bugfixes for v0.4.0)
This commit is contained in:
10
MANIFEST.in
10
MANIFEST.in
@@ -1,8 +1,4 @@
|
|||||||
|
include LICENSE
|
||||||
|
include README.md
|
||||||
include archivebox/VERSION
|
include archivebox/VERSION
|
||||||
graft archivebox/themes
|
recursive-include archivebox/themes *
|
||||||
graft archivebox/themes/static
|
|
||||||
graft archivebox/themes/admin
|
|
||||||
graft archivebox/themes/default
|
|
||||||
graft archivebox/themes/default/static
|
|
||||||
graft archivebox/themes/legacy
|
|
||||||
graft archivebox/themes/legacy/static
|
|
||||||
|
1
Pipfile
1
Pipfile
@@ -12,6 +12,7 @@ setuptools = "*"
|
|||||||
sphinx = "*"
|
sphinx = "*"
|
||||||
recommonmark = "*"
|
recommonmark = "*"
|
||||||
sphinx-rtd-theme = "*"
|
sphinx-rtd-theme = "*"
|
||||||
|
twine = "*"
|
||||||
|
|
||||||
[packages]
|
[packages]
|
||||||
dataclasses = "*"
|
dataclasses = "*"
|
||||||
|
117
Pipfile.lock
generated
117
Pipfile.lock
generated
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"_meta": {
|
"_meta": {
|
||||||
"hash": {
|
"hash": {
|
||||||
"sha256": "8ac4f9e5cd266406a861a283b321b9eee0ca469638f838e93467403ef2f0594d"
|
"sha256": "5a1618caef76ff53b66c5e8674d8e639d25f75068f7026ad799e217d307628fc"
|
||||||
},
|
},
|
||||||
"pipfile-spec": 6,
|
"pipfile-spec": 6,
|
||||||
"requires": {
|
"requires": {
|
||||||
@@ -64,11 +64,11 @@
|
|||||||
},
|
},
|
||||||
"django": {
|
"django": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:7c3543e4fb070d14e10926189a7fcf42ba919263b7473dceaefce34d54e8a119",
|
"sha256:6fcc3cbd55b16f9a01f37de8bcbe286e0ea22e87096557f1511051780338eaea",
|
||||||
"sha256:a2814bffd1f007805b19194eb0b9a331933b82bd5da1c3ba3d7b7ba16e06dc4b"
|
"sha256:bb407d0bb46395ca1241f829f5bd03f7e482f97f7d1936e26e98dacb201ed4ec"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==2.2"
|
"version": "==2.2.1"
|
||||||
},
|
},
|
||||||
"django-extensions": {
|
"django-extensions": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@@ -203,11 +203,11 @@
|
|||||||
},
|
},
|
||||||
"youtube-dl": {
|
"youtube-dl": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:46f6e30c673ba71de84748dad4c264d1b6fb30beebf1ef834846a651b4524a78",
|
"sha256:31844229a4f4d7003e03ab309ff2caff1b16ce0acbd3cfb7a13276058af13056",
|
||||||
"sha256:b20d110e1bed8d16f5771bb938ab6e5da67f08af62b599af65301cca290f2e15"
|
"sha256:a751bd293e2d7ee963910de14b3eb95b88837021899be488fade0b8abe815650"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==2019.4.24"
|
"version": "==2019.4.30"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"develop": {
|
"develop": {
|
||||||
@@ -240,6 +240,13 @@
|
|||||||
],
|
],
|
||||||
"version": "==0.1.0"
|
"version": "==0.1.0"
|
||||||
},
|
},
|
||||||
|
"bleach": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:213336e49e102af26d9cde77dd2d0397afabc5a6bf2fed985dc35b5d1e285a16",
|
||||||
|
"sha256:3fdf7f77adcf649c9911387df51254b813185e32b2c6619f690b593a617e19fa"
|
||||||
|
],
|
||||||
|
"version": "==3.1.0"
|
||||||
|
},
|
||||||
"certifi": {
|
"certifi": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:59b7658e26ca9c7339e00f8f4636cdfe59d34fa37b9b04f6f9e9926b3cece1a5",
|
"sha256:59b7658e26ca9c7339e00f8f4636cdfe59d34fa37b9b04f6f9e9926b3cece1a5",
|
||||||
@@ -256,10 +263,10 @@
|
|||||||
},
|
},
|
||||||
"commonmark": {
|
"commonmark": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:9f6dda7876b2bb88dd784440166f4bc8e56cb2b2551264051123bacb0b6c1d8a",
|
"sha256:14c3df31e8c9c463377e287b2a1eefaa6019ab97b22dad36e2f32be59d61d68d",
|
||||||
"sha256:abcbc854e0eae5deaf52ae5e328501b78b4a0758bf98ac8bb792fce993006084"
|
"sha256:867fc5db078ede373ab811e16b6789e9d033b15ccd7296f370ca52d1ee792ce0"
|
||||||
],
|
],
|
||||||
"version": "==0.8.1"
|
"version": "==0.9.0"
|
||||||
},
|
},
|
||||||
"decorator": {
|
"decorator": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@@ -449,6 +456,13 @@
|
|||||||
],
|
],
|
||||||
"version": "==0.7.5"
|
"version": "==0.7.5"
|
||||||
},
|
},
|
||||||
|
"pkginfo": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:7424f2c8511c186cd5424bbf31045b77435b37a8d604990b79d4e70d741148bb",
|
||||||
|
"sha256:a6d9e40ca61ad3ebd0b72fbadd4fba16e4c0e4df0428c041e01e06eb6ee71f32"
|
||||||
|
],
|
||||||
|
"version": "==1.5.0.1"
|
||||||
|
},
|
||||||
"prompt-toolkit": {
|
"prompt-toolkit": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:11adf3389a996a6d45cc277580d0d53e8a5afd281d0c9ec71b28e6f121463780",
|
"sha256:11adf3389a996a6d45cc277580d0d53e8a5afd281d0c9ec71b28e6f121463780",
|
||||||
@@ -499,6 +513,13 @@
|
|||||||
],
|
],
|
||||||
"version": "==2019.1"
|
"version": "==2019.1"
|
||||||
},
|
},
|
||||||
|
"readme-renderer": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:bb16f55b259f27f75f640acf5e00cf897845a8b3e4731b5c1a436e4b8529202f",
|
||||||
|
"sha256:c8532b79afc0375a85f10433eca157d6b50f7d6990f337fa498c96cd4bfc203d"
|
||||||
|
],
|
||||||
|
"version": "==24.0"
|
||||||
|
},
|
||||||
"recommonmark": {
|
"recommonmark": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:a520b8d25071a51ae23a27cf6252f2fe387f51bdc913390d83b2b50617f5bb48",
|
"sha256:a520b8d25071a51ae23a27cf6252f2fe387f51bdc913390d83b2b50617f5bb48",
|
||||||
@@ -514,6 +535,13 @@
|
|||||||
],
|
],
|
||||||
"version": "==2.21.0"
|
"version": "==2.21.0"
|
||||||
},
|
},
|
||||||
|
"requests-toolbelt": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f",
|
||||||
|
"sha256:968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0"
|
||||||
|
],
|
||||||
|
"version": "==0.9.1"
|
||||||
|
},
|
||||||
"six": {
|
"six": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
|
"sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
|
||||||
@@ -586,6 +614,13 @@
|
|||||||
],
|
],
|
||||||
"version": "==1.1.3"
|
"version": "==1.1.3"
|
||||||
},
|
},
|
||||||
|
"tqdm": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:d385c95361699e5cf7622485d9b9eae2d4864b21cd5a2374a9c381ffed701021",
|
||||||
|
"sha256:e22977e3ebe961f72362f6ddfb9197cc531c9737aaf5f607ef09740c849ecd05"
|
||||||
|
],
|
||||||
|
"version": "==4.31.1"
|
||||||
|
},
|
||||||
"traitlets": {
|
"traitlets": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:9c4bd2d267b7153df9152698efb1050a5d84982d3384a37b2c1f7723ba3e7835",
|
"sha256:9c4bd2d267b7153df9152698efb1050a5d84982d3384a37b2c1f7723ba3e7835",
|
||||||
@@ -593,30 +628,37 @@
|
|||||||
],
|
],
|
||||||
"version": "==4.3.2"
|
"version": "==4.3.2"
|
||||||
},
|
},
|
||||||
|
"twine": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:0fb0bfa3df4f62076cab5def36b1a71a2e4acb4d1fa5c97475b048117b1a6446",
|
||||||
|
"sha256:d6c29c933ecfc74e9b1d9fa13aa1f87c5d5770e119f5a4ce032092f0ff5b14dc"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==1.13.0"
|
||||||
|
},
|
||||||
"typed-ast": {
|
"typed-ast": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:04894d268ba6eab7e093d43107869ad49e7b5ef40d1a94243ea49b352061b200",
|
"sha256:132eae51d6ef3ff4a8c47c393a4ef5ebf0d1aecc96880eb5d6c8ceab7017cc9b",
|
||||||
"sha256:16616ece19daddc586e499a3d2f560302c11f122b9c692bc216e821ae32aa0d0",
|
"sha256:18141c1484ab8784006c839be8b985cfc82a2e9725837b0ecfa0203f71c4e39d",
|
||||||
"sha256:252fdae740964b2d3cdfb3f84dcb4d6247a48a6abe2579e8029ab3be3cdc026c",
|
"sha256:2baf617f5bbbfe73fd8846463f5aeafc912b5ee247f410700245d68525ec584a",
|
||||||
"sha256:2af80a373af123d0b9f44941a46df67ef0ff7a60f95872412a145f4500a7fc99",
|
"sha256:3d90063f2cbbe39177e9b4d888e45777012652d6110156845b828908c51ae462",
|
||||||
"sha256:2c88d0a913229a06282b285f42a31e063c3bf9071ff65c5ea4c12acb6977c6a7",
|
"sha256:4304b2218b842d610aa1a1d87e1dc9559597969acc62ce717ee4dfeaa44d7eee",
|
||||||
"sha256:2ea99c029ebd4b5a308d915cc7fb95b8e1201d60b065450d5d26deb65d3f2bc1",
|
"sha256:4983ede548ffc3541bae49a82675996497348e55bafd1554dc4e4a5d6eda541a",
|
||||||
"sha256:3d2e3ab175fc097d2a51c7a0d3fda442f35ebcc93bb1d7bd9b95ad893e44c04d",
|
"sha256:5315f4509c1476718a4825f45a203b82d7fdf2a6f5f0c8f166435975b1c9f7d4",
|
||||||
"sha256:4766dd695548a15ee766927bf883fb90c6ac8321be5a60c141f18628fb7f8da8",
|
"sha256:6cdfb1b49d5345f7c2b90d638822d16ba62dc82f7616e9b4caa10b72f3f16649",
|
||||||
"sha256:56b6978798502ef66625a2e0f80cf923da64e328da8bbe16c1ff928c70c873de",
|
"sha256:7b325f12635598c604690efd7a0197d0b94b7d7778498e76e0710cd582fd1c7a",
|
||||||
"sha256:5cddb6f8bce14325b2863f9d5ac5c51e07b71b462361fd815d1d7706d3a9d682",
|
"sha256:8d3b0e3b8626615826f9a626548057c5275a9733512b137984a68ba1598d3d2f",
|
||||||
"sha256:644ee788222d81555af543b70a1098f2025db38eaa99226f3a75a6854924d4db",
|
"sha256:8f8631160c79f53081bd23446525db0bc4c5616f78d04021e6e434b286493fd7",
|
||||||
"sha256:64cf762049fc4775efe6b27161467e76d0ba145862802a65eefc8879086fc6f8",
|
"sha256:912de10965f3dc89da23936f1cc4ed60764f712e5fa603a09dd904f88c996760",
|
||||||
"sha256:68c362848d9fb71d3c3e5f43c09974a0ae319144634e7a47db62f0f2a54a7fa7",
|
"sha256:b010c07b975fe853c65d7bbe9d4ac62f1c69086750a574f6292597763781ba18",
|
||||||
"sha256:6c1f3c6f6635e611d58e467bf4371883568f0de9ccc4606f17048142dec14a1f",
|
"sha256:c908c10505904c48081a5415a1e295d8403e353e0c14c42b6d67f8f97fae6616",
|
||||||
"sha256:b213d4a02eec4ddf622f4d2fbc539f062af3788d1f332f028a2e19c42da53f15",
|
"sha256:c94dd3807c0c0610f7c76f078119f4ea48235a953512752b9175f9f98f5ae2bd",
|
||||||
"sha256:bb27d4e7805a7de0e35bd0cb1411bc85f807968b2b0539597a49a23b00a622ae",
|
"sha256:ce65dee7594a84c466e79d7fb7d3303e7295d16a83c22c7c4037071b059e2c21",
|
||||||
"sha256:c9d414512eaa417aadae7758bc118868cd2396b0e6138c1dd4fda96679c079d3",
|
"sha256:eaa9cfcb221a8a4c2889be6f93da141ac777eb8819f077e1d09fb12d00a09a93",
|
||||||
"sha256:f0937165d1e25477b01081c4763d2d9cdc3b18af69cb259dd4f640c9b900fe5e",
|
"sha256:f3376bc31bad66d46d44b4e6522c5c21976bf9bca4ef5987bb2bf727f4506cbb",
|
||||||
"sha256:fb96a6e2c11059ecf84e6741a319f93f683e440e341d4489c9b161eca251cf2a",
|
"sha256:f9202fa138544e13a4ec1a6792c35834250a85958fde1251b6a22e07d1260ae7"
|
||||||
"sha256:fc71d2d6ae56a091a8d94f33ec9d0f2001d1cb1db423d8b4355debfe9ce689b7"
|
|
||||||
],
|
],
|
||||||
"version": "==1.3.4"
|
"version": "==1.3.5"
|
||||||
},
|
},
|
||||||
"typing-extensions": {
|
"typing-extensions": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@@ -628,10 +670,10 @@
|
|||||||
},
|
},
|
||||||
"urllib3": {
|
"urllib3": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:4c291ca23bbb55c76518905869ef34bdd5f0e46af7afe6861e8375643ffee1a0",
|
"sha256:2393a695cd12afedd0dcb26fe5d50d0cf248e5a66f75dbd89a3d4eb333a61af4",
|
||||||
"sha256:9a247273df709c4fedb38c711e44292304f73f39ab01beda9f6b9fc375669ac3"
|
"sha256:a637e5fae88995b256e3409dc4d52c2e2e0ba32c42a6365fee8bbd2238de3cfb"
|
||||||
],
|
],
|
||||||
"version": "==1.24.2"
|
"version": "==1.24.3"
|
||||||
},
|
},
|
||||||
"wcwidth": {
|
"wcwidth": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@@ -639,6 +681,13 @@
|
|||||||
"sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c"
|
"sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c"
|
||||||
],
|
],
|
||||||
"version": "==0.1.7"
|
"version": "==0.1.7"
|
||||||
|
},
|
||||||
|
"webencodings": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78",
|
||||||
|
"sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"
|
||||||
|
],
|
||||||
|
"version": "==0.5.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1 +1 @@
|
|||||||
0.4.0
|
0.4.2
|
||||||
|
@@ -44,10 +44,19 @@ CONFIG_DEFAULTS: Dict[str, ConfigDefaultDict] = {
|
|||||||
'TIMEOUT': {'type': int, 'default': 60},
|
'TIMEOUT': {'type': int, 'default': 60},
|
||||||
'MEDIA_TIMEOUT': {'type': int, 'default': 3600},
|
'MEDIA_TIMEOUT': {'type': int, 'default': 3600},
|
||||||
'OUTPUT_PERMISSIONS': {'type': str, 'default': '755'},
|
'OUTPUT_PERMISSIONS': {'type': str, 'default': '755'},
|
||||||
'FOOTER_INFO': {'type': str, 'default': 'Content is hosted for personal archiving purposes only. Contact server owner for any takedown requests.'},
|
|
||||||
'URL_BLACKLIST': {'type': str, 'default': None},
|
'URL_BLACKLIST': {'type': str, 'default': None},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'SERVER_CONFIG': {
|
||||||
|
'SECRET_KEY': {'type': str, 'default': None},
|
||||||
|
'ALLOWED_HOSTS': {'type': str, 'default': '*'},
|
||||||
|
'DEBUG': {'type': bool, 'default': False},
|
||||||
|
'PUBLIC_INDEX': {'type': bool, 'default': True},
|
||||||
|
'PUBLIC_SNAPSHOTS': {'type': bool, 'default': True},
|
||||||
|
'FOOTER_INFO': {'type': str, 'default': 'Content is hosted for personal archiving purposes only. Contact server owner for any takedown requests.'},
|
||||||
|
'ACTIVE_THEME': {'type': str, 'default': 'default'},
|
||||||
|
},
|
||||||
|
|
||||||
'ARCHIVE_METHOD_TOGGLES': {
|
'ARCHIVE_METHOD_TOGGLES': {
|
||||||
'SAVE_TITLE': {'type': bool, 'default': True, 'aliases': ('FETCH_TITLE',)},
|
'SAVE_TITLE': {'type': bool, 'default': True, 'aliases': ('FETCH_TITLE',)},
|
||||||
'SAVE_FAVICON': {'type': bool, 'default': True, 'aliases': ('FETCH_FAVICON',)},
|
'SAVE_FAVICON': {'type': bool, 'default': True, 'aliases': ('FETCH_FAVICON',)},
|
||||||
@@ -313,9 +322,6 @@ def write_config_file(config: Dict[str, str], out_dir: str=None) -> ConfigDict:
|
|||||||
with open(config_path, 'w+') as f:
|
with open(config_path, 'w+') as f:
|
||||||
f.write(CONFIG_HEADER)
|
f.write(CONFIG_HEADER)
|
||||||
|
|
||||||
if not config:
|
|
||||||
return {}
|
|
||||||
|
|
||||||
config_file = ConfigParser()
|
config_file = ConfigParser()
|
||||||
config_file.optionxform = str
|
config_file.optionxform = str
|
||||||
config_file.read(config_path)
|
config_file.read(config_path)
|
||||||
@@ -336,6 +342,21 @@ def write_config_file(config: Dict[str, str], out_dir: str=None) -> ConfigDict:
|
|||||||
|
|
||||||
config_file[section] = {**existing_config, key: val}
|
config_file[section] = {**existing_config, key: val}
|
||||||
|
|
||||||
|
# always make sure there's a SECRET_KEY defined for Django
|
||||||
|
existing_secret_key = None
|
||||||
|
if 'SERVER_CONFIG' in config_file and 'SECRET_KEY' in config_file['SERVER_CONFIG']:
|
||||||
|
existing_secret_key = config_file['SERVER_CONFIG']['SECRET_KEY']
|
||||||
|
|
||||||
|
if (not existing_secret_key) or ('not a valid secret' in existing_secret_key):
|
||||||
|
from django.utils.crypto import get_random_string
|
||||||
|
chars = 'abcdefghijklmnopqrstuvwxyz0123456789-_+!.'
|
||||||
|
random_secret_key = get_random_string(50, chars)
|
||||||
|
if 'SERVER_CONFIG' in config_file:
|
||||||
|
config_file['SERVER_CONFIG']['SECRET_KEY'] = random_secret_key
|
||||||
|
else:
|
||||||
|
config_file['SERVER_CONFIG'] = {'SECRET_KEY': random_secret_key}
|
||||||
|
|
||||||
|
f.write(CONFIG_HEADER)
|
||||||
config_file.write(f)
|
config_file.write(f)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@@ -22,9 +22,16 @@ class ConfigDict(BaseConfig, total=False):
|
|||||||
TIMEOUT: int
|
TIMEOUT: int
|
||||||
MEDIA_TIMEOUT: int
|
MEDIA_TIMEOUT: int
|
||||||
OUTPUT_PERMISSIONS: str
|
OUTPUT_PERMISSIONS: str
|
||||||
FOOTER_INFO: str
|
|
||||||
URL_BLACKLIST: Optional[str]
|
URL_BLACKLIST: Optional[str]
|
||||||
|
|
||||||
|
SECRET_KEY: str
|
||||||
|
ALLOWED_HOSTS: str
|
||||||
|
DEBUG: bool
|
||||||
|
PUBLIC_INDEX: bool
|
||||||
|
PUBLIC_SNAPSHOTS: bool
|
||||||
|
FOOTER_INFO: str
|
||||||
|
ACTIVE_THEME: str
|
||||||
|
|
||||||
SAVE_TITLE: bool
|
SAVE_TITLE: bool
|
||||||
SAVE_FAVICON: bool
|
SAVE_FAVICON: bool
|
||||||
SAVE_WGET: bool
|
SAVE_WGET: bool
|
||||||
|
@@ -3,26 +3,25 @@ __package__ = 'archivebox.core'
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
SECRET_KEY = '---------------- not a valid secret key ! ----------------'
|
|
||||||
DEBUG = os.getenv('DEBUG', 'False').lower() == 'true'
|
|
||||||
ALLOWED_HOSTS = ['*']
|
|
||||||
|
|
||||||
REPO_DIR = os.path.abspath(os.path.join(os.path.abspath(__file__), os.path.pardir, os.path.pardir))
|
from ..config import (
|
||||||
OUTPUT_DIR = os.path.abspath(os.getenv('OUTPUT_DIR', os.curdir))
|
OUTPUT_DIR,
|
||||||
ARCHIVE_DIR = os.path.join(OUTPUT_DIR, 'archive')
|
SECRET_KEY,
|
||||||
DATABASE_FILE = os.path.join(OUTPUT_DIR, 'index.sqlite3')
|
DEBUG,
|
||||||
|
ALLOWED_HOSTS,
|
||||||
|
PYTHON_DIR,
|
||||||
|
ACTIVE_THEME,
|
||||||
|
SQL_INDEX_FILENAME,
|
||||||
|
)
|
||||||
|
|
||||||
ACTIVE_THEME = 'default'
|
|
||||||
|
|
||||||
|
ALLOWED_HOSTS = ALLOWED_HOSTS.split(',')
|
||||||
IS_SHELL = 'shell' in sys.argv[:3] or 'shell_plus' in sys.argv[:3]
|
IS_SHELL = 'shell' in sys.argv[:3] or 'shell_plus' in sys.argv[:3]
|
||||||
|
|
||||||
APPEND_SLASH = True
|
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
'django.contrib.auth',
|
'django.contrib.auth',
|
||||||
'django.contrib.contenttypes',
|
'django.contrib.contenttypes',
|
||||||
'django.contrib.sessions',
|
'django.contrib.sessions',
|
||||||
# 'django.contrib.sites',
|
|
||||||
'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
'django.contrib.admin',
|
'django.contrib.admin',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
@@ -40,17 +39,17 @@ MIDDLEWARE = [
|
|||||||
'django.middleware.csrf.CsrfViewMiddleware',
|
'django.middleware.csrf.CsrfViewMiddleware',
|
||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
# 'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
ROOT_URLCONF = 'core.urls'
|
ROOT_URLCONF = 'core.urls'
|
||||||
|
APPEND_SLASH = True
|
||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
{
|
{
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||||
'DIRS': [
|
'DIRS': [
|
||||||
os.path.join(REPO_DIR, 'themes', ACTIVE_THEME),
|
os.path.join(PYTHON_DIR, 'themes', ACTIVE_THEME),
|
||||||
os.path.join(REPO_DIR, 'themes', 'default'),
|
os.path.join(PYTHON_DIR, 'themes', 'default'),
|
||||||
os.path.join(REPO_DIR, 'themes'),
|
os.path.join(PYTHON_DIR, 'themes'),
|
||||||
],
|
],
|
||||||
'APP_DIRS': True,
|
'APP_DIRS': True,
|
||||||
'OPTIONS': {
|
'OPTIONS': {
|
||||||
@@ -69,7 +68,7 @@ WSGI_APPLICATION = 'core.wsgi.application'
|
|||||||
DATABASES = {
|
DATABASES = {
|
||||||
'default': {
|
'default': {
|
||||||
'ENGINE': 'django.db.backends.sqlite3',
|
'ENGINE': 'django.db.backends.sqlite3',
|
||||||
'NAME': DATABASE_FILE,
|
'NAME': os.path.join(OUTPUT_DIR, SQL_INDEX_FILENAME),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,7 +103,7 @@ SHELL_PLUS_PRINT_SQL = False
|
|||||||
IPYTHON_ARGUMENTS = ['--no-confirm-exit', '--no-banner']
|
IPYTHON_ARGUMENTS = ['--no-confirm-exit', '--no-banner']
|
||||||
IPYTHON_KERNEL_DISPLAY_NAME = 'ArchiveBox Django Shell'
|
IPYTHON_KERNEL_DISPLAY_NAME = 'ArchiveBox Django Shell'
|
||||||
if IS_SHELL:
|
if IS_SHELL:
|
||||||
os.environ['PYTHONSTARTUP'] = os.path.join(REPO_DIR, 'core', 'welcome_message.py')
|
os.environ['PYTHONSTARTUP'] = os.path.join(PYTHON_DIR, 'core', 'welcome_message.py')
|
||||||
|
|
||||||
|
|
||||||
LANGUAGE_CODE = 'en-us'
|
LANGUAGE_CODE = 'en-us'
|
||||||
@@ -118,11 +117,7 @@ EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
|||||||
|
|
||||||
STATIC_URL = '/static/'
|
STATIC_URL = '/static/'
|
||||||
STATICFILES_DIRS = [
|
STATICFILES_DIRS = [
|
||||||
os.path.join(REPO_DIR, 'themes', ACTIVE_THEME, 'static'),
|
os.path.join(PYTHON_DIR, 'themes', ACTIVE_THEME, 'static'),
|
||||||
os.path.join(REPO_DIR, 'themes', 'default', 'static'),
|
os.path.join(PYTHON_DIR, 'themes', 'default', 'static'),
|
||||||
os.path.join(REPO_DIR, 'themes', 'static'),
|
os.path.join(PYTHON_DIR, 'themes', 'static'),
|
||||||
]
|
]
|
||||||
|
|
||||||
SERVE_STATIC = True
|
|
||||||
|
|
||||||
|
|
||||||
|
@@ -22,8 +22,14 @@ urlpatterns = [
|
|||||||
path('add/', AddLinks.as_view(), name='AddLinks'),
|
path('add/', AddLinks.as_view(), name='AddLinks'),
|
||||||
|
|
||||||
path('static/<path>', views.serve),
|
path('static/<path>', views.serve),
|
||||||
|
|
||||||
|
path('accounts/login/', RedirectView.as_view(url='/admin/login/')),
|
||||||
|
path('accounts/logout/', RedirectView.as_view(url='/admin/logout/')),
|
||||||
|
|
||||||
path('accounts/', include('django.contrib.auth.urls')),
|
path('accounts/', include('django.contrib.auth.urls')),
|
||||||
path('admin/', admin.site.urls),
|
path('admin/', admin.site.urls),
|
||||||
|
|
||||||
|
|
||||||
path('', MainIndex.as_view(), name='Home'),
|
path('', MainIndex.as_view(), name='Home'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@@ -4,11 +4,18 @@ from django.shortcuts import render, redirect
|
|||||||
|
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.views import View, static
|
from django.views import View, static
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
from core.models import Snapshot
|
from core.models import Snapshot
|
||||||
|
|
||||||
from ..index import load_main_index, load_main_index_meta
|
from ..index import load_main_index, load_main_index_meta
|
||||||
from ..config import OUTPUT_DIR, VERSION, FOOTER_INFO
|
from ..config import (
|
||||||
|
OUTPUT_DIR,
|
||||||
|
VERSION,
|
||||||
|
FOOTER_INFO,
|
||||||
|
PUBLIC_INDEX,
|
||||||
|
PUBLIC_SNAPSHOTS,
|
||||||
|
)
|
||||||
from ..util import base_url
|
from ..util import base_url
|
||||||
|
|
||||||
|
|
||||||
@@ -16,6 +23,9 @@ class MainIndex(View):
|
|||||||
template = 'main_index.html'
|
template = 'main_index.html'
|
||||||
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
|
if not request.user.is_authenticated and not PUBLIC_INDEX:
|
||||||
|
return redirect(f'/admin/login/?next={request.path}')
|
||||||
|
|
||||||
all_links = load_main_index(out_dir=OUTPUT_DIR)
|
all_links = load_main_index(out_dir=OUTPUT_DIR)
|
||||||
meta_info = load_main_index_meta(out_dir=OUTPUT_DIR)
|
meta_info = load_main_index_meta(out_dir=OUTPUT_DIR)
|
||||||
|
|
||||||
@@ -34,6 +44,9 @@ class AddLinks(View):
|
|||||||
template = 'add_links.html'
|
template = 'add_links.html'
|
||||||
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
|
if not request.user.is_authenticated and not PUBLIC_INDEX:
|
||||||
|
return redirect(f'/admin/login/?next={request.path}')
|
||||||
|
|
||||||
context = {}
|
context = {}
|
||||||
|
|
||||||
return render(template_name=self.template, request=request, context=context)
|
return render(template_name=self.template, request=request, context=context)
|
||||||
@@ -54,6 +67,9 @@ class LinkDetails(View):
|
|||||||
if '/' not in path:
|
if '/' not in path:
|
||||||
return redirect(f'{path}/index.html')
|
return redirect(f'{path}/index.html')
|
||||||
|
|
||||||
|
if not request.user.is_authenticated and not PUBLIC_SNAPSHOTS:
|
||||||
|
return redirect(f'/admin/login/?next={request.path}')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
slug, archivefile = path.split('/', 1)
|
slug, archivefile = path.split('/', 1)
|
||||||
except (IndexError, ValueError):
|
except (IndexError, ValueError):
|
||||||
|
@@ -59,6 +59,7 @@ class ArchiveResult:
|
|||||||
}
|
}
|
||||||
info['start_ts'] = parse_date(info['start_ts'])
|
info['start_ts'] = parse_date(info['start_ts'])
|
||||||
info['end_ts'] = parse_date(info['end_ts'])
|
info['end_ts'] = parse_date(info['end_ts'])
|
||||||
|
info['cmd_version'] = info.get('cmd_version')
|
||||||
return cls(**info)
|
return cls(**info)
|
||||||
|
|
||||||
def to_dict(self, *keys) -> dict:
|
def to_dict(self, *keys) -> dict:
|
||||||
|
@@ -292,14 +292,14 @@ def init(force: bool=False, out_dir: str=OUTPUT_DIR) -> None:
|
|||||||
|
|
||||||
setup_django(out_dir, check_db=False)
|
setup_django(out_dir, check_db=False)
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
assert settings.DATABASE_FILE == os.path.join(out_dir, SQL_INDEX_FILENAME)
|
DATABASE_FILE = os.path.join(out_dir, SQL_INDEX_FILENAME)
|
||||||
print(f' √ {settings.DATABASE_FILE}')
|
print(f' √ {DATABASE_FILE}')
|
||||||
print()
|
print()
|
||||||
for migration_line in apply_migrations(out_dir):
|
for migration_line in apply_migrations(out_dir):
|
||||||
print(f' {migration_line}')
|
print(f' {migration_line}')
|
||||||
|
|
||||||
|
|
||||||
assert os.path.exists(settings.DATABASE_FILE)
|
assert os.path.exists(DATABASE_FILE)
|
||||||
|
|
||||||
# from django.contrib.auth.models import User
|
# from django.contrib.auth.models import User
|
||||||
# if IS_TTY and not User.objects.filter(is_superuser=True).exists():
|
# if IS_TTY and not User.objects.filter(is_superuser=True).exists():
|
||||||
|
@@ -3,6 +3,20 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
# if you're a developer working on archivebox, still prefer the archivebox
|
||||||
|
# versions of ./manage.py commands whenever possible. When that's not possible
|
||||||
|
# (e.g. makemigrations), you can comment out this check temporarily
|
||||||
|
|
||||||
|
print("[X] Don't run ./manage.py directly, use the archivebox CLI instead e.g.:")
|
||||||
|
print(' archivebox manage createsuperuser')
|
||||||
|
print()
|
||||||
|
print(' Hint: Use these archivebox commands instead of the ./manage.py equivalents:')
|
||||||
|
print(' archivebox init (migrates the databse to latest version)')
|
||||||
|
print(' archivebox server (runs the Django web server)')
|
||||||
|
print(' archivebox shell (opens an iPython Django shell with all models imported)')
|
||||||
|
print(' archivebox manage [cmd] (any other management commands)')
|
||||||
|
raise SystemExit(2)
|
||||||
|
|
||||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'core.settings')
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'core.settings')
|
||||||
try:
|
try:
|
||||||
from django.core.management import execute_from_command_line
|
from django.core.management import execute_from_command_line
|
||||||
|
@@ -190,7 +190,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-sm-10" style="text-align: right">
|
<div class="col-sm-10" style="text-align: right">
|
||||||
<a href="/add/">Add Links</a> |
|
<a href="/add/">Add Links</a> |
|
||||||
<a href="/admin/core/page/">Admin</a> |
|
<a href="/admin/core/snapshot/">Admin</a> |
|
||||||
<a href="https://github.com/pirate/ArchiveBox/wiki">Docs</a>
|
<a href="https://github.com/pirate/ArchiveBox/wiki">Docs</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -10,6 +10,7 @@ from urllib.request import Request, urlopen
|
|||||||
from urllib.parse import urlparse, quote, unquote
|
from urllib.parse import urlparse, quote, unquote
|
||||||
from html import escape, unescape
|
from html import escape, unescape
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from dateutil import parser as dateparser
|
||||||
|
|
||||||
from base32_crockford import encode as base32_encode # type: ignore
|
from base32_crockford import encode as base32_encode # type: ignore
|
||||||
import json as pyjson
|
import json as pyjson
|
||||||
@@ -140,50 +141,7 @@ def parse_date(date: Any) -> Optional[datetime]:
|
|||||||
date = str(date)
|
date = str(date)
|
||||||
|
|
||||||
if isinstance(date, str):
|
if isinstance(date, str):
|
||||||
if date.replace('.', '').isdigit():
|
return dateparser.parse(date)
|
||||||
# this is a brittle attempt at unix timestamp parsing (which is
|
|
||||||
# notoriously hard to do). It may lead to dates being off by
|
|
||||||
# anything from hours to decades, depending on which app, OS,
|
|
||||||
# and sytem time configuration was used for the original timestamp
|
|
||||||
# more info: https://github.com/pirate/ArchiveBox/issues/119
|
|
||||||
|
|
||||||
# Note: always always always store the original timestamp string
|
|
||||||
# somewhere indepentendly of the parsed datetime, so that later
|
|
||||||
# bugs dont repeatedly misparse and rewrite increasingly worse dates.
|
|
||||||
# the correct date can always be re-derived from the timestamp str
|
|
||||||
timestamp = float(date)
|
|
||||||
|
|
||||||
EARLIEST_POSSIBLE = 473403600.0 # 1985
|
|
||||||
LATEST_POSSIBLE = 1735707600.0 # 2025
|
|
||||||
|
|
||||||
if EARLIEST_POSSIBLE < timestamp < LATEST_POSSIBLE:
|
|
||||||
# number is seconds
|
|
||||||
return datetime.fromtimestamp(timestamp)
|
|
||||||
|
|
||||||
elif EARLIEST_POSSIBLE * 1000 < timestamp < LATEST_POSSIBLE * 1000:
|
|
||||||
# number is milliseconds
|
|
||||||
return datetime.fromtimestamp(timestamp / 1000)
|
|
||||||
|
|
||||||
elif EARLIEST_POSSIBLE * 1000*1000 < timestamp < LATEST_POSSIBLE * 1000*1000:
|
|
||||||
# number is microseconds
|
|
||||||
return datetime.fromtimestamp(timestamp / (1000*1000))
|
|
||||||
|
|
||||||
else:
|
|
||||||
# continue to the end and raise a parsing failed error.
|
|
||||||
# we dont want to even attempt parsing timestamp strings that
|
|
||||||
# arent within these ranges
|
|
||||||
pass
|
|
||||||
|
|
||||||
if '-' in date:
|
|
||||||
# 2019-04-07T05:44:39.227520
|
|
||||||
try:
|
|
||||||
return datetime.fromisoformat(date)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
try:
|
|
||||||
return datetime.strptime(date, '%Y-%m-%d %H:%M')
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
raise ValueError('Tried to parse invalid date! {}'.format(date))
|
raise ValueError('Tried to parse invalid date! {}'.format(date))
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
# This is the example default configiration file for ArchiveBox.
|
# This is the example default configiration file for ArchiveBox.
|
||||||
#
|
#
|
||||||
# Copy example config from here into your project's ArchiveBox.conf file,
|
# Copy lines from here into your project's ArchiveBox.conf file and uncomment,
|
||||||
# DO NOT EDIT THIS FILE DIRECTLY!
|
# DO NOT EDIT THIS FILE DIRECTLY!
|
||||||
#
|
#
|
||||||
# See the list of all the possible options. documentation, and examples here:
|
# See the list of all the possible options. documentation, and examples here:
|
||||||
@@ -11,10 +11,17 @@
|
|||||||
# ONLY_NEW = False
|
# ONLY_NEW = False
|
||||||
# TIMEOUT = 60
|
# TIMEOUT = 60
|
||||||
# MEDIA_TIMEOUT = 3600
|
# MEDIA_TIMEOUT = 3600
|
||||||
# ACTIVE_THEME = default
|
|
||||||
# FOOTER_INFO = Content is hosted for personal archiving purposes only. Contact server owner for any takedown requests.
|
|
||||||
# URL_BLACKLIST = (://(.*\.)?facebook\.com)|(://(.*\.)?ebay\.com)|(.*\.exe$)
|
# URL_BLACKLIST = (://(.*\.)?facebook\.com)|(://(.*\.)?ebay\.com)|(.*\.exe$)
|
||||||
|
|
||||||
|
[SERVER_CONFIG]
|
||||||
|
# SECRET_KEY = ---------------- not a valid secret key ! ----------------
|
||||||
|
# DEBUG = False
|
||||||
|
# PUBLIC_INDEX = True
|
||||||
|
# PUBLIC_SNAPSHOTS = True
|
||||||
|
# FOOTER_INFO = Content is hosted for personal archiving purposes only. Contact server owner for any takedown requests.
|
||||||
|
# ACTIVE_THEME = default
|
||||||
|
|
||||||
|
|
||||||
[ARCHIVE_METHOD_TOGGLES]
|
[ARCHIVE_METHOD_TOGGLES]
|
||||||
# SAVE_TITLE = True
|
# SAVE_TITLE = True
|
||||||
# SAVE_FAVICON = True
|
# SAVE_FAVICON = True
|
||||||
|
50
setup.py
50
setup.py
@@ -1,40 +1,34 @@
|
|||||||
import os
|
import os
|
||||||
import setuptools
|
import setuptools
|
||||||
|
|
||||||
with open("README.md", "r") as fh:
|
BASE_DIR = os.path.abspath(os.path.dirname(os.path.abspath(__file__)))
|
||||||
long_description = fh.read()
|
PYTHON_DIR = os.path.join(BASE_DIR, 'archivebox')
|
||||||
|
|
||||||
|
with open('README.md', "r") as f:
|
||||||
|
README = f.read()
|
||||||
|
|
||||||
script_dir = os.path.abspath(os.path.dirname(os.path.abspath(__file__)))
|
with open(os.path.join(PYTHON_DIR, 'VERSION'), 'r') as f:
|
||||||
|
VERSION = f.read().strip()
|
||||||
|
|
||||||
VERSION = open(os.path.join(script_dir, 'archivebox', 'VERSION'), 'r').read().strip()
|
|
||||||
try:
|
|
||||||
GIT_HEAD = open(os.path.join(script_dir, '.git', 'HEAD'), 'r').read().strip().split(': ')[1]
|
|
||||||
GIT_SHA = open(os.path.join(script_dir, '.git', GIT_HEAD), 'r').read().strip()[:9]
|
|
||||||
PYPI_VERSION = "{}+{}".format(VERSION, GIT_SHA)
|
|
||||||
except:
|
|
||||||
PYPI_VERSION = VERSION
|
|
||||||
|
|
||||||
with open(os.path.join(script_dir, 'archivebox', 'VERSION'), 'w+') as f:
|
|
||||||
f.write(PYPI_VERSION)
|
|
||||||
|
|
||||||
setuptools.setup(
|
setuptools.setup(
|
||||||
name="archivebox",
|
name="archivebox",
|
||||||
version=PYPI_VERSION,
|
version=VERSION,
|
||||||
author="Nick Sweeting",
|
author="Nick Sweeting",
|
||||||
author_email="git@nicksweeting.com",
|
author_email="git@nicksweeting.com",
|
||||||
description="The self-hosted internet archive.",
|
description="The self-hosted internet archive.",
|
||||||
long_description=long_description,
|
long_description=README,
|
||||||
long_description_content_type="text/markdown",
|
long_description_content_type="text/markdown",
|
||||||
url="https://github.com/pirate/ArchiveBox",
|
url="https://github.com/pirate/ArchiveBox",
|
||||||
|
license='MIT',
|
||||||
project_urls={
|
project_urls={
|
||||||
'Documentation': 'https://github.com/pirate/ArchiveBox/Wiki',
|
'Donate': 'https://github.com/pirate/ArchiveBox/wiki/Donations',
|
||||||
'Community': 'https://github.com/pirate/ArchiveBox/wiki/Web-Archiving-Community',
|
|
||||||
'Source': 'https://github.com/pirate/ArchiveBox',
|
|
||||||
'Bug Tracker': 'https://github.com/pirate/ArchiveBox/issues',
|
|
||||||
'Roadmap': 'https://github.com/pirate/ArchiveBox/wiki/Roadmap',
|
|
||||||
'Changelog': 'https://github.com/pirate/ArchiveBox/wiki/Changelog',
|
'Changelog': 'https://github.com/pirate/ArchiveBox/wiki/Changelog',
|
||||||
'Patreon': 'https://github.com/pirate/ArchiveBox/wiki/Donations',
|
'Roadmap': 'https://github.com/pirate/ArchiveBox/wiki/Roadmap',
|
||||||
|
'Bug Tracker': 'https://github.com/pirate/ArchiveBox/issues',
|
||||||
|
'Source': 'https://github.com/pirate/ArchiveBox',
|
||||||
|
'Community': 'https://github.com/pirate/ArchiveBox/wiki/Web-Archiving-Community',
|
||||||
|
'Documentation': 'https://github.com/pirate/ArchiveBox/Wiki',
|
||||||
},
|
},
|
||||||
packages=setuptools.find_packages(),
|
packages=setuptools.find_packages(),
|
||||||
python_requires='>=3.6',
|
python_requires='>=3.6',
|
||||||
@@ -61,19 +55,7 @@ setuptools.setup(
|
|||||||
'archivebox = archivebox.__main__:main',
|
'archivebox = archivebox.__main__:main',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
package_data={
|
include_package_data=True,
|
||||||
'archivebox': [
|
|
||||||
# Manifest.ini must correspond 1:1 with this list
|
|
||||||
'VERSION',
|
|
||||||
'themes/*',
|
|
||||||
'themes/static/*',
|
|
||||||
'themes/admin/*'
|
|
||||||
'themes/default/*'
|
|
||||||
'themes/default/static/*'
|
|
||||||
'themes/legacy/*',
|
|
||||||
'themes/legacy/static/*',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
classifiers=[
|
classifiers=[
|
||||||
"Development Status :: 4 - Beta",
|
"Development Status :: 4 - Beta",
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user