migrate to python3, flask1 and pipenv/pipfiles.
This commit is contained in:
parent
882224d741
commit
f1a7ba9908
|
@ -0,0 +1,39 @@
|
|||
[[source]]
|
||||
verify_ssl = true
|
||||
name = "pypi"
|
||||
url = "https://pypi.org/simple"
|
||||
|
||||
[dev-packages]
|
||||
fakeredis = { version = "*" }
|
||||
httpretty = { version = "*" }
|
||||
python-dotenv = { version = "*" }
|
||||
mock = { version = "*" }
|
||||
|
||||
[requires]
|
||||
python_version = "3.6"
|
||||
|
||||
[packages]
|
||||
celery = "==4.1.0"
|
||||
gunicorn = "==19.6.0"
|
||||
hashids = "==1.2.0"
|
||||
"psycopg2" = "==2.7"
|
||||
redis = "==2.10.5"
|
||||
requests = "==2.1.0"
|
||||
stripe = "==1.55.0"
|
||||
structlog = "==16.1.0"
|
||||
pyaml = "==17.12.1"
|
||||
SQLAlchemy = "==1.0.13"
|
||||
Flask = "==1"
|
||||
Flask-CDN = "==1.5.3"
|
||||
Flask-Cors = "==3.0.2"
|
||||
Flask-Limiter = "==0.9.3"
|
||||
Flask-Login = "==0.3.0"
|
||||
Flask-Migrate = "==2.0.3"
|
||||
Flask-Redis = "==0.0.6"
|
||||
Flask-Script = "==2.0.5"
|
||||
Flask-SQLAlchemy = "==2.1"
|
||||
Flask-Testing = "==0.6.1"
|
||||
unicodecsv = "==0.14.1"
|
||||
|
||||
[pipenv]
|
||||
allow_prereleases = true
|
|
@ -0,0 +1,390 @@
|
|||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "cc2b2db95dc7263c3ea29d7abb715712854572e32a6c17aa1dfa418a604cd8cc"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
"python_version": "3.6"
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"name": "pypi",
|
||||
"url": "https://pypi.org/simple",
|
||||
"verify_ssl": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"default": {
|
||||
"alembic": {
|
||||
"hashes": [
|
||||
"sha256:85bd3ea7633024e4930900bc64fb58f9742dedbc6ebb6ecf25be2ea9a3c1b32e"
|
||||
],
|
||||
"version": "==0.9.9"
|
||||
},
|
||||
"amqp": {
|
||||
"hashes": [
|
||||
"sha256:4e28d3ea61a64ae61830000c909662cb053642efddbe96503db0e7783a6ee85b",
|
||||
"sha256:cba1ace9d4ff6049b190d8b7991f9c1006b443a5238021aca96dd6ad2ac9da22"
|
||||
],
|
||||
"version": "==2.2.2"
|
||||
},
|
||||
"billiard": {
|
||||
"hashes": [
|
||||
"sha256:1d7b22bdc47aa52841120fcd22a74ae4fc8c13e9d3935643098184f5788c3ce6",
|
||||
"sha256:abd9ce008c9a71ccde2c816f8daa36246e92a21e6a799831b887d88277187ecd"
|
||||
],
|
||||
"version": "==3.5.0.3"
|
||||
},
|
||||
"celery": {
|
||||
"hashes": [
|
||||
"sha256:77ff3730198d6a17b3c1f05579ebe570b579efb35f6d7e13dba3b1368d068b35",
|
||||
"sha256:81a67f0d53a688ec2bc8557bd5d6d7218f925a6f2e6df80e01560de9e28997ec"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.1.0"
|
||||
},
|
||||
"click": {
|
||||
"hashes": [
|
||||
"sha256:29f99fc6125fbc931b758dc053b3114e55c77a6e4c6c3a2674a2dc986016381d",
|
||||
"sha256:f15516df478d5a56180fbf80e68f206010e6d160fc39fa508b65e035fd75130b"
|
||||
],
|
||||
"version": "==6.7"
|
||||
},
|
||||
"flask": {
|
||||
"hashes": [
|
||||
"sha256:7fab1062d11dd0038434e790d18c5b9133fd9e6b7257d707c4578ccc1e38b67c",
|
||||
"sha256:b1883637bbee4dc7bc98d900792d0a304d609fce0f5bd9ca91d1b6457e5918dd"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.0"
|
||||
},
|
||||
"flask-cdn": {
|
||||
"hashes": [
|
||||
"sha256:2f1c55dbd42a5f2d7ceb110064819befb4eaf87ed1b353286971f8a72322a2cd"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.5.3"
|
||||
},
|
||||
"flask-cors": {
|
||||
"hashes": [
|
||||
"sha256:0a09f3559ded4759387dfa2a355de59bc161f67269a1f4b7b0712a64b1f7dad6",
|
||||
"sha256:149a2cca7bb5785324ed12afc0f327050f4e5e397113359b1390e4430b55b3dd",
|
||||
"sha256:66a78dd308157f17296a254f793eba4cda1f75bfc96d9c5e33ad12bf9a411287"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.0.2"
|
||||
},
|
||||
"flask-limiter": {
|
||||
"hashes": [
|
||||
"sha256:8fe5cb813fc3887dcebc53b0952b2116a4fe557b80aa4e008063800448bbc4d9",
|
||||
"sha256:e909793a6d52138aef026c2b40ffae8609889db11c3bfd4dfe1b906d9a06a8b8"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.9.3"
|
||||
},
|
||||
"flask-login": {
|
||||
"hashes": [
|
||||
"sha256:f2f144cdadfd03ca84cb65c3a5162a21b57262bebdd83e6db20077d460c45d52"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.3.0"
|
||||
},
|
||||
"flask-migrate": {
|
||||
"hashes": [
|
||||
"sha256:331f1facf93712b6a3067eac382e645b1ef09e9a6d34da447acb6a3c293afd80"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.0.3"
|
||||
},
|
||||
"flask-redis": {
|
||||
"hashes": [
|
||||
"sha256:f0d49837581bfb9985f9af73620d3eebec5ca24d7cfe3578a3ae33e698a56488"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.0.6"
|
||||
},
|
||||
"flask-script": {
|
||||
"hashes": [
|
||||
"sha256:cef76eac751396355429a14c38967bb14d4973c53e07dec94af5cc8fb017107f"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.0.5"
|
||||
},
|
||||
"flask-sqlalchemy": {
|
||||
"hashes": [
|
||||
"sha256:c5244de44cc85d2267115624d83faef3f9e8f088756788694f305a5d5ad137c5"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.1"
|
||||
},
|
||||
"flask-testing": {
|
||||
"hashes": [
|
||||
"sha256:abf539332c013aee5301cbb720d2c6a78bb69fe9bcf854697b6f62f1e7f175b2"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.6.1"
|
||||
},
|
||||
"gunicorn": {
|
||||
"hashes": [
|
||||
"sha256:723234ea1fa8dff370ab69830ba8bc37469a7cba13fd66055faeef24085e6530",
|
||||
"sha256:813f6916d18a4c8e90efde72f419308b357692f81333cb1125f80013d22fb618"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==19.6.0"
|
||||
},
|
||||
"hashids": {
|
||||
"hashes": [
|
||||
"sha256:6539b892a426e75747a9c0ad69409e9566f9c21b79310fc3424b5b6726f28da6"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.2.0"
|
||||
},
|
||||
"itsdangerous": {
|
||||
"hashes": [
|
||||
"sha256:cbb3fcf8d3e33df861709ecaf89d9e6629cff0a217bc2848f1b41cd30d360519"
|
||||
],
|
||||
"version": "==0.24"
|
||||
},
|
||||
"jinja2": {
|
||||
"hashes": [
|
||||
"sha256:74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd",
|
||||
"sha256:f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4"
|
||||
],
|
||||
"version": "==2.10"
|
||||
},
|
||||
"kombu": {
|
||||
"hashes": [
|
||||
"sha256:01f0da9fe222a2183345004243d1518c0fbe5875955f1b24842f2d9c65709ade",
|
||||
"sha256:4249d9dd9dbf1fcec471d1c2def20653c9310dd1a217272d77e4844f9d5273cb"
|
||||
],
|
||||
"version": "==4.1.0"
|
||||
},
|
||||
"limits": {
|
||||
"hashes": [
|
||||
"sha256:9df578f4161017d79f5188609f1d65f6b639f8aad2914c3960c9252e56a0ff95",
|
||||
"sha256:a017b8d9e9da6761f4574642149c337f8f540d4edfe573fb91ad2c4001a2bc76"
|
||||
],
|
||||
"version": "==1.3"
|
||||
},
|
||||
"mako": {
|
||||
"hashes": [
|
||||
"sha256:4e02fde57bd4abb5ec400181e4c314f56ac3e49ba4fb8b0d50bba18cb27d25ae"
|
||||
],
|
||||
"version": "==1.0.7"
|
||||
},
|
||||
"markupsafe": {
|
||||
"hashes": [
|
||||
"sha256:a6be69091dac236ea9c6bc7d012beab42010fa914c459791d627dad4910eb665"
|
||||
],
|
||||
"version": "==1.0"
|
||||
},
|
||||
"psycopg2": {
|
||||
"hashes": [
|
||||
"sha256:076351ed3c3b36ea147d456b3eb568be0ec89a29b000349d5b6d8e3e3026f49e",
|
||||
"sha256:11b5e4f9d1e7e742d6a953e70d19ac5a12db5dc539db929657df676ec475b435",
|
||||
"sha256:23198892998282d06884897dcb7561c043371c2b33118f1d12c0abbe1d7fd1d5",
|
||||
"sha256:25c843b5ba383820a027603f3ef7b4d9854602d325295cd82bded15b33b762c9",
|
||||
"sha256:3474c77a657fc2344f415ac0b5dc053ee0b3cdc84bbcc7cfc8047604b981febc",
|
||||
"sha256:4460dc9d0132db3483fcedd4f2fb30475fd89a98b9cd563f6ad1dabd59247845",
|
||||
"sha256:45dd9b3f08e327f23f5e70075738a69f4851c83e91b90b9fe3fe873de8c4e953",
|
||||
"sha256:58041e8372f077ed4dd37029e6988b2f0859e520d3949a90c4d6456ec47f7c75",
|
||||
"sha256:5e1d71b33e1712ab691988cd8d8866196e981fd3e353e01ff4604c437b338c75",
|
||||
"sha256:6f3b5bf74396800738f8c15e6cecf7ce64d0e59f80e1ca5fe5ceedbcdeaac5ed",
|
||||
"sha256:6ff610d8a5f44f28c27191cbc9025e4a6f8922dbb99b9d3ef0779b9c5ca164d6",
|
||||
"sha256:707b40dc972223880364fce6eaa42e8fd7946f636e91fc7ccf65beb149e9a489",
|
||||
"sha256:72448b57b82ece42a7f6c1c50a5ca27c0521f9e31b5d3621d19b2d9434debb32",
|
||||
"sha256:7d8e4c2639a693a3b87942596a68b2cfc6cfbd66c0997b1adb7b8828bde6c1b7",
|
||||
"sha256:84cb4591cd8a1238ddabcf8b066d3179fb2e2296679b720e8272f267d9137783",
|
||||
"sha256:868a251a32f46c2615e034541bf61944b810ba66a2a8efeef7fbeb35d20ba246",
|
||||
"sha256:87c950f463a11341af2228bc4173d4c47dce2e4d4b9282715b923c8d90b7547d",
|
||||
"sha256:8cdfef6ca3359f66418c84778506e431fd02a0a4cee10c8f54affef141d9a37f",
|
||||
"sha256:8e875e864876bf69879221746bacc3479cd5db26245bac4c3b2f9622ee6ac52f",
|
||||
"sha256:96c08e0d72a1dd7a9314636351a3ed8bdcdf332ec8414da93e81c2cb747ca62d",
|
||||
"sha256:99b02d34029d163344490212ebd7987b75a18349f5e1925ea77a1e77dd700c12",
|
||||
"sha256:ab4f2a7d9c43c65ea62f28f549079d3f5f5d5ca54eafb552c765537718a8dbd4",
|
||||
"sha256:ac8d1231aae6b6849e732e8d021a5f93fa9d8d8bac02c663797a9e6c5ac1607d",
|
||||
"sha256:c946dd53fd39427051e72707146643660f017388641b9daa2d316fe45af06551",
|
||||
"sha256:ceadecf660ad4f7a31ea5baef30a7351add8626f9fd3daaafabb9a9e549f3f9a",
|
||||
"sha256:da8475270687816c500a5874e14b25e120c2e9711dc6f7cf697af6bda31ab741",
|
||||
"sha256:e0313fb08c883ebc772d11c9ecf0b4ecf8946a37ed6fb3d2b6ad96f833a1af2b",
|
||||
"sha256:e452672f74f8d4540da0b9df4d50cd26109f05e2ed35015bb0ffe6a0f08e730e",
|
||||
"sha256:ebabc4ab0bd3fb4e4644cfe5c11c3ba22991ce1c6d40b4755a5f3f144e97e822",
|
||||
"sha256:ec26d70bc9e5be78334f3df474bb852e078b80d2ac44a069a925f4333a432cc9",
|
||||
"sha256:f50d0df01a1fe2c0b8e8d74c9eb8f636d09ccea4c7f50eb267eb117bd33b8516"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.7"
|
||||
},
|
||||
"pyaml": {
|
||||
"hashes": [
|
||||
"sha256:66623c52f34d83a2c0fc963e08e8b9d0c13d88404e3b43b1852ef71eda19afa3",
|
||||
"sha256:f83fc302c52c6b83a15345792693ae0b5bc07ad19f59e318b7617d7123d62990"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==17.12.1"
|
||||
},
|
||||
"python-dateutil": {
|
||||
"hashes": [
|
||||
"sha256:1adb80e7a782c12e52ef9a8182bebeb73f1d7e24e374397af06fb4956c8dc5c0",
|
||||
"sha256:e27001de32f627c22380a688bcc43ce83504a7bc5da472209b4c70f02829f0b8"
|
||||
],
|
||||
"version": "==2.7.3"
|
||||
},
|
||||
"python-editor": {
|
||||
"hashes": [
|
||||
"sha256:a3c066acee22a1c94f63938341d4fb374e3fdd69366ed6603d7b24bed1efc565"
|
||||
],
|
||||
"version": "==1.0.3"
|
||||
},
|
||||
"pytz": {
|
||||
"hashes": [
|
||||
"sha256:65ae0c8101309c45772196b21b74c46b2e5d11b6275c45d251b150d5da334555",
|
||||
"sha256:c06425302f2cf668f1bba7a0a03f3c1d34d4ebeef2c72003da308b3947c7f749"
|
||||
],
|
||||
"version": "==2018.4"
|
||||
},
|
||||
"pyyaml": {
|
||||
"hashes": [
|
||||
"sha256:0c507b7f74b3d2dd4d1322ec8a94794927305ab4cebbe89cc47fe5e81541e6e8",
|
||||
"sha256:16b20e970597e051997d90dc2cddc713a2876c47e3d92d59ee198700c5427736",
|
||||
"sha256:3262c96a1ca437e7e4763e2843746588a965426550f3797a79fca9c6199c431f",
|
||||
"sha256:326420cbb492172dec84b0f65c80942de6cedb5233c413dd824483989c000608",
|
||||
"sha256:4474f8ea030b5127225b8894d626bb66c01cda098d47a2b0d3429b6700af9fd8",
|
||||
"sha256:592766c6303207a20efc445587778322d7f73b161bd994f227adaa341ba212ab",
|
||||
"sha256:5ac82e411044fb129bae5cfbeb3ba626acb2af31a8d17d175004b70862a741a7",
|
||||
"sha256:5f84523c076ad14ff5e6c037fe1c89a7f73a3e04cf0377cb4d017014976433f3",
|
||||
"sha256:827dc04b8fa7d07c44de11fabbc888e627fa8293b695e0f99cb544fdfa1bf0d1",
|
||||
"sha256:b4c423ab23291d3945ac61346feeb9a0dc4184999ede5e7c43e1ffb975130ae6",
|
||||
"sha256:bc6bced57f826ca7cb5125a10b23fd0f2fff3b7c4701d64c439a300ce665fff8",
|
||||
"sha256:c01b880ec30b5a6e6aa67b09a2fe3fb30473008c85cd6a67359a1b15ed6d83a4",
|
||||
"sha256:ca233c64c6e40eaa6c66ef97058cdc80e8d0157a443655baa1b2966e812807ca",
|
||||
"sha256:e863072cdf4c72eebf179342c94e6989c67185842d9997960b3e69290b2fa269"
|
||||
],
|
||||
"version": "==3.12"
|
||||
},
|
||||
"redis": {
|
||||
"hashes": [
|
||||
"sha256:5dfbae6acfc54edf0a7a415b99e0b21c0a3c27a7f787b292eea727b1facc5533",
|
||||
"sha256:97156b37d7cda4e7d8658be1148c983984e1a975090ba458cc7e244025191dbd"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.10.5"
|
||||
},
|
||||
"requests": {
|
||||
"hashes": [
|
||||
"sha256:a57307f3a5f35ec9e1254aaf3e0484063ee3ee6b5f123fb35c5b2673492efa71",
|
||||
"sha256:fcef306d62b1c061eb00b8402cf136ff0ea1daf7a53b60cdef9563a22850072c"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.1.0"
|
||||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
"sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9",
|
||||
"sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb"
|
||||
],
|
||||
"version": "==1.11.0"
|
||||
},
|
||||
"sqlalchemy": {
|
||||
"hashes": [
|
||||
"sha256:e755fd23b8bd574163d392ae85f41f6cd32eca8fe5bd7b5692de77265bb220cf"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.0.13"
|
||||
},
|
||||
"stripe": {
|
||||
"hashes": [
|
||||
"sha256:74ef15145feba5fdfad2311195bf500971f04397756d4dcb0715173e928ccf1e",
|
||||
"sha256:7545301c65459ab65546ef1f3d58d2a0500d2820214af2e8a21f89d1517047bf",
|
||||
"sha256:d395e13ded4ae0c13872e5dc4402e20b46f8c2eafa9ec035756347dac53a0a45"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.55.0"
|
||||
},
|
||||
"structlog": {
|
||||
"hashes": [
|
||||
"sha256:a8cc45c208c6b251031ec223940552eccd97dbdb322474e0c4ec9e50f76a225a",
|
||||
"sha256:b44dfaadcbab84e6bb97bd9b263f61534a79611014679757cd93e2359ee7be01"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==16.1.0"
|
||||
},
|
||||
"unicodecsv": {
|
||||
"hashes": [
|
||||
"sha256:018c08037d48649a0412063ff4eda26eaa81eff1546dbffa51fa5293276ff7fc"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.14.1"
|
||||
},
|
||||
"vine": {
|
||||
"hashes": [
|
||||
"sha256:52116d59bc45392af9fdd3b75ed98ae48a93e822cee21e5fda249105c59a7a72",
|
||||
"sha256:6849544be74ec3638e84d90bc1cf2e1e9224cc10d96cd4383ec3f69e9bce077b"
|
||||
],
|
||||
"version": "==1.1.4"
|
||||
},
|
||||
"werkzeug": {
|
||||
"hashes": [
|
||||
"sha256:c3fd7a7d41976d9f44db327260e263132466836cef6f91512889ed60ad26557c",
|
||||
"sha256:d5da73735293558eb1651ee2fddc4d0dedcfa06538b8813a2e20011583c9e49b"
|
||||
],
|
||||
"version": "==0.14.1"
|
||||
}
|
||||
},
|
||||
"develop": {
|
||||
"fakeredis": {
|
||||
"hashes": [
|
||||
"sha256:26631f046ecc327140176af595835fb8b5cae27b0bc0adcc4a84ba286fcad487",
|
||||
"sha256:488e6654ec57ef96f7f24227196602bac227d838b4a28407f113525ec0449310"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.10.3"
|
||||
},
|
||||
"httpretty": {
|
||||
"hashes": [
|
||||
"sha256:69259e22addf5ab5f25bd00c6568e16fc2e54efdd4da69eb0950718dba3b2dab"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.9.4"
|
||||
},
|
||||
"mock": {
|
||||
"hashes": [
|
||||
"sha256:5ce3c71c5545b472da17b72268978914d0252980348636840bd34a00b5cc96c1",
|
||||
"sha256:b158b6df76edd239b8208d481dc46b6afd45a846b7812ff0ce58971cf5bc8bba"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.0.0"
|
||||
},
|
||||
"pbr": {
|
||||
"hashes": [
|
||||
"sha256:4e8a0ed6a8705a26768f4c3da26026013b157821fe5f95881599556ea9d91c19",
|
||||
"sha256:dae4aaa78eafcad10ce2581fc34d694faa616727837fd8e55c1a00951ad6744f"
|
||||
],
|
||||
"version": "==4.0.2"
|
||||
},
|
||||
"python-dotenv": {
|
||||
"hashes": [
|
||||
"sha256:4965ed170bf51c347a89820e8050655e9c25db3837db6602e906b6d850fad85c",
|
||||
"sha256:509736185257111613009974e666568a1b031b028b61b500ef1ab4ee780089d5"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.8.2"
|
||||
},
|
||||
"redis": {
|
||||
"hashes": [
|
||||
"sha256:5dfbae6acfc54edf0a7a415b99e0b21c0a3c27a7f787b292eea727b1facc5533",
|
||||
"sha256:97156b37d7cda4e7d8658be1148c983984e1a975090ba458cc7e244025191dbd"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.10.5"
|
||||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
"sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9",
|
||||
"sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb"
|
||||
],
|
||||
"version": "==1.11.0"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from .create_app import create_app
|
||||
app = create_app()
|
||||
|
||||
from app import create_app
|
||||
forms_app = create_app()
|
||||
from . import manage
|
||||
|
|
|
@ -1,26 +1,14 @@
|
|||
import json
|
||||
import stripe
|
||||
import structlog
|
||||
|
||||
from flask import Flask, g, request, redirect
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from flask_login import LoginManager, current_user
|
||||
from flask_cdn import CDN
|
||||
from flask_redis import Redis
|
||||
from flask_limiter import Limiter
|
||||
from flask_limiter.util import get_ipaddr
|
||||
from celery import Celery
|
||||
import settings
|
||||
|
||||
DB = SQLAlchemy()
|
||||
redis_store = Redis()
|
||||
stripe.api_key = settings.STRIPE_SECRET_KEY
|
||||
cdn = CDN()
|
||||
celery = Celery(__name__, broker=settings.CELERY_BROKER_URL)
|
||||
|
||||
import routes
|
||||
from users.models import User
|
||||
|
||||
from . import routes, settings
|
||||
from .stuff import DB, redis_store, cdn, celery
|
||||
from .users.models import User
|
||||
|
||||
def configure_login(app):
|
||||
login_manager = LoginManager()
|
||||
|
@ -62,7 +50,7 @@ def configure_logger(app):
|
|||
|
||||
rest = []
|
||||
for k, v in event.items():
|
||||
if type(v) is unicode:
|
||||
if type(v) is str:
|
||||
v = v.encode('utf-8', 'ignore')
|
||||
rest.append('\x1b[{}m{}\x1b[0m={}'.format(
|
||||
levelcolor,
|
|
@ -1 +0,0 @@
|
|||
import views
|
|
@ -1,15 +1,14 @@
|
|||
import werkzeug.datastructures
|
||||
import urlparse
|
||||
import requests
|
||||
import hashlib
|
||||
import hashids
|
||||
import uuid
|
||||
import json
|
||||
from urlparse import urljoin
|
||||
from urllib.parse import urljoin, urlparse
|
||||
from flask import request, g
|
||||
|
||||
from formspree import settings
|
||||
from formspree.app import redis_store, DB
|
||||
from formspree.stuff import redis_store, DB
|
||||
from flask import jsonify
|
||||
from flask_login import current_user
|
||||
|
||||
|
@ -44,7 +43,7 @@ def ordered_storage(f):
|
|||
def referrer_to_path(r):
|
||||
if not r:
|
||||
return ''
|
||||
parsed = urlparse.urlparse(r)
|
||||
parsed = urlparse(r)
|
||||
n = parsed.netloc + parsed.path
|
||||
return n
|
||||
|
||||
|
@ -52,7 +51,7 @@ def referrer_to_path(r):
|
|||
def referrer_to_baseurl(r):
|
||||
if not r:
|
||||
return ''
|
||||
parsed = urlparse.urlparse(r)
|
||||
parsed = urlparse(r)
|
||||
n = parsed.netloc
|
||||
return n
|
||||
|
||||
|
@ -66,7 +65,7 @@ def http_form_to_dict(data):
|
|||
ret = {}
|
||||
ordered_keys = []
|
||||
|
||||
for elem in data.iteritems(multi=True):
|
||||
for elem in data.items(multi=True):
|
||||
if not elem[0] in ret.keys():
|
||||
ret[elem[0]] = []
|
||||
ordered_keys.append(elem[0])
|
||||
|
@ -181,4 +180,4 @@ def check_valid_form_settings_request(form):
|
|||
if not form:
|
||||
return jsonify(error='That form does not exist. Please check the link and try again.'), 400
|
||||
|
||||
return True
|
||||
return True
|
||||
|
|
|
@ -3,16 +3,17 @@ import random
|
|||
import hashlib
|
||||
import datetime
|
||||
|
||||
from formspree.app import DB, redis_store
|
||||
from formspree import settings
|
||||
from formspree.utils import send_email, unix_time_for_12_months_from_now, \
|
||||
next_url, IS_VALID_EMAIL, request_wants_json
|
||||
from flask import url_for, render_template, g
|
||||
from sqlalchemy.sql.expression import delete
|
||||
from sqlalchemy import func
|
||||
from werkzeug.datastructures import ImmutableMultiDict, \
|
||||
ImmutableOrderedMultiDict
|
||||
from helpers import HASH, HASHIDS_CODEC, REDIS_COUNTER_KEY, \
|
||||
|
||||
from formspree import settings
|
||||
from formspree.stuff import DB, redis_store
|
||||
from formspree.utils import send_email, unix_time_for_12_months_from_now, \
|
||||
next_url, IS_VALID_EMAIL, request_wants_json
|
||||
from .helpers import HASH, HASHIDS_CODEC, REDIS_COUNTER_KEY, \
|
||||
http_form_to_dict, referrer_to_path, \
|
||||
store_first_submission, fetch_first_submission, \
|
||||
KEYS_NOT_STORED
|
||||
|
@ -390,14 +391,14 @@ class Form(DB.Model):
|
|||
def unconfirm_digest(self):
|
||||
return hmac.new(
|
||||
settings.NONCE_SECRET,
|
||||
'id={}'.format(self.id),
|
||||
'id={}'.format(self.id).encode('utf-8'),
|
||||
hashlib.sha256
|
||||
).hexdigest()
|
||||
|
||||
def unconfirm_with_digest(self, digest):
|
||||
if hmac.new(
|
||||
settings.NONCE_SECRET,
|
||||
'id={}'.format(self.id),
|
||||
'id={}'.format(self.id).encode('utf-8'),
|
||||
hashlib.sha256
|
||||
).hexdigest() != digest:
|
||||
return False
|
||||
|
|
|
@ -10,20 +10,20 @@ from flask import request, url_for, render_template, redirect, \
|
|||
abort
|
||||
from flask_login import current_user, login_required
|
||||
from flask_cors import cross_origin
|
||||
from urlparse import urljoin
|
||||
from jinja2.exceptions import TemplateNotFound
|
||||
from urllib.parse import urljoin
|
||||
|
||||
from formspree import settings
|
||||
from formspree.app import DB
|
||||
from formspree.stuff import DB
|
||||
from formspree.utils import request_wants_json, jsonerror, IS_VALID_EMAIL, \
|
||||
url_domain, valid_url
|
||||
from helpers import http_form_to_dict, ordered_storage, referrer_to_path, \
|
||||
from .helpers import http_form_to_dict, ordered_storage, referrer_to_path, \
|
||||
remove_www, referrer_to_baseurl, sitewide_file_check, \
|
||||
verify_captcha, temp_store_hostname, get_temp_hostname, \
|
||||
HASH, assign_ajax, valid_domain_request, \
|
||||
KEYS_NOT_STORED, KEYS_EXCLUDED_FROM_EMAIL, check_valid_form_settings_request
|
||||
from models import Form, Submission
|
||||
|
||||
from jinja2.exceptions import TemplateNotFound
|
||||
KEYS_NOT_STORED, KEYS_EXCLUDED_FROM_EMAIL, \
|
||||
check_valid_form_settings_request
|
||||
from .models import Form, Submission
|
||||
|
||||
|
||||
def thanks():
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
import os
|
||||
import datetime
|
||||
import click
|
||||
|
||||
from flask_script import prompt_bool
|
||||
from flask_migrate import Migrate
|
||||
|
||||
from formspree import app, settings
|
||||
from formspree.stuff import redis_store, DB
|
||||
from formspree.forms.helpers import REDIS_COUNTER_KEY
|
||||
from formspree.forms.models import Form
|
||||
|
||||
from celery.bin.celery import main as celery_main
|
||||
|
||||
# add flask-migrate commands
|
||||
migrate = Migrate(app, DB)
|
||||
|
||||
@app.cli.command()
|
||||
def run_debug(port=os.getenv('PORT', 5000)):
|
||||
'''runs the app with debug flag set to true'''
|
||||
app.run(host='0.0.0.0', debug=True, port=int(port))
|
||||
|
||||
@app.cli.command()
|
||||
@click.option('-i', '--id', default=None, help='form id')
|
||||
@click.option('-H', '--host', default=None, help='referer hostname')
|
||||
@click.option('-e', '--email', default=None, help='form email')
|
||||
def monthly_counters(email=None, host=None, id=None, month=datetime.date.today().month):
|
||||
if id:
|
||||
query = [Form.query.get(id)]
|
||||
elif email and host:
|
||||
query = Form.query.filter_by(email=email, host=host)
|
||||
elif email and not host:
|
||||
query = Form.query.filter_by(email=email)
|
||||
elif host and not email:
|
||||
query = Form.query.filter_by(host=host)
|
||||
else:
|
||||
print('supply each --email or --form or both (or --id).')
|
||||
return 1
|
||||
|
||||
for form in query:
|
||||
nsubmissions = redis_store.get(REDIS_COUNTER_KEY(form_id=form.id, month=month)) \
|
||||
or 0
|
||||
print('%s submissions for %s' % (nsubmissions, form))
|
||||
|
||||
|
||||
@app.cli.command()
|
||||
@click.option('-t', '--testname', 'testname', default=None, help='name of test')
|
||||
def test(testname=None):
|
||||
import unittest
|
||||
|
||||
test_loader = unittest.defaultTestLoader
|
||||
if testname:
|
||||
test_suite = test_loader.loadTestsFromName(testname)
|
||||
else:
|
||||
test_suite = test_loader.discover('.')
|
||||
|
||||
test_runner = unittest.TextTestRunner()
|
||||
test_runner.run(test_suite)
|
||||
|
||||
@app.cli.command()
|
||||
def celery():
|
||||
celery_args = ['celery', 'worker', '-B', '-s', '/tmp/celery.db', '--concurrency=5']
|
||||
return celery_main(celery_args)
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
|
@ -1,55 +1,55 @@
|
|||
import forms
|
||||
import users
|
||||
import static_pages
|
||||
import formspree.forms.views as fv
|
||||
import formspree.users.views as uv
|
||||
import formspree.static_pages.views as sv
|
||||
|
||||
def configure_routes(app):
|
||||
app.add_url_rule('/', 'index', view_func=static_pages.views.default, methods=['GET'])
|
||||
app.add_url_rule('/favicon.ico', view_func=static_pages.views.favicon)
|
||||
app.add_url_rule('/formspree-verify.txt', view_func=static_pages.views.formspree_verify)
|
||||
app.add_url_rule('/', 'index', view_func=sv.default, methods=['GET'])
|
||||
app.add_url_rule('/favicon.ico', view_func=sv.favicon)
|
||||
app.add_url_rule('/formspree-verify.txt', view_func=sv.formspree_verify)
|
||||
|
||||
# Public forms
|
||||
app.add_url_rule('/<email_or_string>', 'send', view_func=forms.views.send, methods=['GET', 'POST'])
|
||||
app.add_url_rule('/unblock/<email>', 'unblock_email', view_func=forms.views.unblock_email, methods=['GET', 'POST'])
|
||||
app.add_url_rule('/resend/<email>', 'resend_confirmation', view_func=forms.views.resend_confirmation, methods=['POST'])
|
||||
app.add_url_rule('/confirm/<nonce>', 'confirm_email', view_func=forms.views.confirm_email, methods=['GET'])
|
||||
app.add_url_rule('/unconfirm/<digest>/<form_id>', 'unconfirm_form', view_func=forms.views.unconfirm_form, methods=['POST'])
|
||||
app.add_url_rule('/thanks', 'thanks', view_func=forms.views.thanks, methods=['GET'])
|
||||
app.add_url_rule('/<email_or_string>', 'send', view_func=fv.send, methods=['GET', 'POST'])
|
||||
app.add_url_rule('/unblock/<email>', 'unblock_email', view_func=fv.unblock_email, methods=['GET', 'POST'])
|
||||
app.add_url_rule('/resend/<email>', 'resend_confirmation', view_func=fv.resend_confirmation, methods=['POST'])
|
||||
app.add_url_rule('/confirm/<nonce>', 'confirm_email', view_func=fv.confirm_email, methods=['GET'])
|
||||
app.add_url_rule('/unconfirm/<digest>/<form_id>', 'unconfirm_form', view_func=fv.unconfirm_form, methods=['POST'])
|
||||
app.add_url_rule('/thanks', 'thanks', view_func=fv.thanks, methods=['GET'])
|
||||
|
||||
# Users
|
||||
app.add_url_rule('/account', 'account', view_func=users.views.account, methods=['GET'])
|
||||
app.add_url_rule('/account/upgrade', 'account-upgrade', view_func=users.views.upgrade, methods=['POST'])
|
||||
app.add_url_rule('/account/downgrade', view_func=users.views.downgrade, methods=['POST'])
|
||||
app.add_url_rule('/account/resubscribe', view_func=users.views.resubscribe, methods=['POST'])
|
||||
app.add_url_rule('/card/add', 'add-card', view_func=users.views.add_card, methods=['POST'])
|
||||
app.add_url_rule('/card/<cardid>/default', 'change-default-card', view_func=users.views.change_default_card, methods=['POST'])
|
||||
app.add_url_rule('/card/<cardid>/delete', 'delete-card', view_func=users.views.delete_card, methods=['POST'])
|
||||
app.add_url_rule('/account/billing', 'billing-dashboard', view_func=users.views.billing, methods=['GET'])
|
||||
app.add_url_rule('/account/billing/invoice/update-invoice-address', 'update-invoice-address', view_func=users.views.update_invoice_address, methods=['POST'])
|
||||
app.add_url_rule('/account/billing/invoice/<invoice_id>', view_func=users.views.invoice, methods=['GET'])
|
||||
app.add_url_rule('/account/add-email', 'add-account-email', view_func=users.views.add_email, methods=['POST'])
|
||||
app.add_url_rule('/account/confirm/<digest>', 'confirm-account-email', view_func=users.views.confirm_email, methods=['GET'])
|
||||
app.add_url_rule('/register', 'register', view_func=users.views.register, methods=['GET', 'POST'])
|
||||
app.add_url_rule('/login', 'login', view_func=users.views.login, methods= ['GET', 'POST'])
|
||||
app.add_url_rule('/login/reset', 'forgot-password', view_func=users.views.forgot_password, methods=['GET', 'POST'])
|
||||
app.add_url_rule('/login/reset/<digest>', 'reset-password', view_func=users.views.reset_password, methods=['GET', 'POST'])
|
||||
app.add_url_rule('/logout', 'logout', view_func=users.views.logout, methods=['GET'])
|
||||
app.add_url_rule('/account', 'account', view_func=uv.account, methods=['GET'])
|
||||
app.add_url_rule('/account/upgrade', 'account-upgrade', view_func=uv.upgrade, methods=['POST'])
|
||||
app.add_url_rule('/account/downgrade', view_func=uv.downgrade, methods=['POST'])
|
||||
app.add_url_rule('/account/resubscribe', view_func=uv.resubscribe, methods=['POST'])
|
||||
app.add_url_rule('/card/add', 'add-card', view_func=uv.add_card, methods=['POST'])
|
||||
app.add_url_rule('/card/<cardid>/default', 'change-default-card', view_func=uv.change_default_card, methods=['POST'])
|
||||
app.add_url_rule('/card/<cardid>/delete', 'delete-card', view_func=uv.delete_card, methods=['POST'])
|
||||
app.add_url_rule('/account/billing', 'billing-dashboard', view_func=uv.billing, methods=['GET'])
|
||||
app.add_url_rule('/account/billing/invoice/update-invoice-address', 'update-invoice-address', view_func=uv.update_invoice_address, methods=['POST'])
|
||||
app.add_url_rule('/account/billing/invoice/<invoice_id>', view_func=uv.invoice, methods=['GET'])
|
||||
app.add_url_rule('/account/add-email', 'add-account-email', view_func=uv.add_email, methods=['POST'])
|
||||
app.add_url_rule('/account/confirm/<digest>', 'confirm-account-email', view_func=uv.confirm_email, methods=['GET'])
|
||||
app.add_url_rule('/register', 'register', view_func=uv.register, methods=['GET', 'POST'])
|
||||
app.add_url_rule('/login', 'login', view_func=uv.login, methods= ['GET', 'POST'])
|
||||
app.add_url_rule('/login/reset', 'forgot-password', view_func=uv.forgot_password, methods=['GET', 'POST'])
|
||||
app.add_url_rule('/login/reset/<digest>', 'reset-password', view_func=uv.reset_password, methods=['GET', 'POST'])
|
||||
app.add_url_rule('/logout', 'logout', view_func=uv.logout, methods=['GET'])
|
||||
|
||||
# Users' forms
|
||||
app.add_url_rule('/dashboard', 'dashboard', view_func=forms.views.forms, methods=['GET'])
|
||||
app.add_url_rule('/forms', 'forms', view_func=forms.views.forms, methods=['GET'])
|
||||
app.add_url_rule('/forms', 'create-form', view_func=forms.views.create_form, methods=['POST'])
|
||||
app.add_url_rule('/forms/sitewide-check', view_func=forms.views.sitewide_check, methods=['GET'])
|
||||
app.add_url_rule('/forms/<hashid>/', 'form-submissions', view_func=forms.views.form_submissions, methods=['GET'])
|
||||
app.add_url_rule('/forms/<hashid>.<format>', 'form-submissions', view_func=forms.views.form_submissions, methods=['GET'])
|
||||
app.add_url_rule('/forms/<hashid>/toggle-recaptcha', 'toggle-recaptcha', view_func=forms.views.form_recaptcha_toggle, methods=['POST'])
|
||||
app.add_url_rule('/forms/<hashid>/toggle-emails', 'toggle-emails', view_func=forms.views.form_email_notification_toggle, methods=['POST'])
|
||||
app.add_url_rule('/forms/<hashid>/toggle-storage', 'toggle-storage', view_func=forms.views.form_archive_toggle, methods=['POST'])
|
||||
app.add_url_rule('/forms/<hashid>/toggle', 'form-toggle', view_func=forms.views.form_toggle, methods=['POST'])
|
||||
app.add_url_rule('/forms/<hashid>/delete', 'form-deletion', view_func=forms.views.form_deletion, methods=['POST'])
|
||||
app.add_url_rule('/forms/<hashid>/delete/<submissionid>', 'submission-deletion', view_func=forms.views.submission_deletion, methods=['POST'])
|
||||
app.add_url_rule('/dashboard', 'dashboard', view_func=fv.forms, methods=['GET'])
|
||||
app.add_url_rule('/forms', 'forms', view_func=fv.forms, methods=['GET'])
|
||||
app.add_url_rule('/forms', 'create-form', view_func=fv.create_form, methods=['POST'])
|
||||
app.add_url_rule('/forms/sitewide-check', view_func=fv.sitewide_check, methods=['GET'])
|
||||
app.add_url_rule('/forms/<hashid>/', 'form-submissions', view_func=fv.form_submissions, methods=['GET'])
|
||||
app.add_url_rule('/forms/<hashid>.<format>', 'form-submissions', view_func=fv.form_submissions, methods=['GET'])
|
||||
app.add_url_rule('/forms/<hashid>/toggle-recaptcha', 'toggle-recaptcha', view_func=fv.form_recaptcha_toggle, methods=['POST'])
|
||||
app.add_url_rule('/forms/<hashid>/toggle-emails', 'toggle-emails', view_func=fv.form_email_notification_toggle, methods=['POST'])
|
||||
app.add_url_rule('/forms/<hashid>/toggle-storage', 'toggle-storage', view_func=fv.form_archive_toggle, methods=['POST'])
|
||||
app.add_url_rule('/forms/<hashid>/toggle', 'form-toggle', view_func=fv.form_toggle, methods=['POST'])
|
||||
app.add_url_rule('/forms/<hashid>/delete', 'form-deletion', view_func=fv.form_deletion, methods=['POST'])
|
||||
app.add_url_rule('/forms/<hashid>/delete/<submissionid>', 'submission-deletion', view_func=fv.submission_deletion, methods=['POST'])
|
||||
|
||||
# Webhooks
|
||||
app.add_url_rule('/webhooks/stripe', view_func=users.views.stripe_webhook, methods=['POST'])
|
||||
app.add_url_rule('/webhooks/stripe', view_func=uv.stripe_webhook, methods=['POST'])
|
||||
|
||||
# Any other static pages and 404
|
||||
app.add_url_rule('/<path:template>', 'default', view_func=static_pages.views.default, methods=['GET'])
|
||||
app.add_url_rule('/<path:template>', 'default', view_func=sv.default, methods=['GET'])
|
||||
|
|
|
@ -13,9 +13,9 @@ SQLALCHEMY_TRACK_MODIFICATIONS = False
|
|||
|
||||
LOG_LEVEL = os.getenv('LOG_LEVEL') or 'debug'
|
||||
|
||||
SECRET_KEY = os.getenv('SECRET_KEY')
|
||||
NONCE_SECRET = os.getenv('NONCE_SECRET')
|
||||
HASHIDS_SALT = os.getenv('HASHIDS_SALT')
|
||||
SECRET_KEY = os.getenv('SECRET_KEY') or ''
|
||||
HASHIDS_SALT = os.getenv('HASHIDS_SALT') or ''
|
||||
NONCE_SECRET = (os.getenv('NONCE_SECRET') or '').encode('utf-8')
|
||||
|
||||
MONTHLY_SUBMISSIONS_LIMIT = int(os.getenv('MONTHLY_SUBMISSIONS_LIMIT') or 1000)
|
||||
ARCHIVED_SUBMISSIONS_LIMIT = int(os.getenv('ARCHIVED_SUBMISSIONS_LIMIT') or 100)
|
||||
|
@ -54,4 +54,4 @@ CONTACT_FORM_HASHID = os.getenv('CONTACT_FORM_HASHID', CONTACT_EMAIL)
|
|||
|
||||
TYPEKIT_KEY = os.getenv('TYPEKIT_KEY', '1234567')
|
||||
|
||||
CELERY_BROKER_URL = os.getenv('REDIS_URL')
|
||||
CELERY_BROKER_URL = os.getenv('REDIS_URL')
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
import views
|
|
@ -0,0 +1,13 @@
|
|||
import stripe
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from flask_cdn import CDN
|
||||
from flask_redis import Redis
|
||||
from celery import Celery
|
||||
|
||||
from . import settings
|
||||
|
||||
DB = SQLAlchemy()
|
||||
redis_store = Redis()
|
||||
stripe.api_key = settings.STRIPE_SECRET_KEY
|
||||
cdn = CDN()
|
||||
celery = Celery(__name__, broker=settings.CELERY_BROKER_URL)
|
|
@ -1 +0,0 @@
|
|||
import views
|
|
@ -4,9 +4,9 @@ from datetime import datetime
|
|||
from flask import url_for, render_template, g
|
||||
|
||||
from formspree import settings
|
||||
from formspree.stuff import DB
|
||||
from formspree.utils import send_email, IS_VALID_EMAIL
|
||||
from formspree.app import DB
|
||||
from helpers import hash_pwd
|
||||
from .helpers import hash_pwd
|
||||
|
||||
class User(DB.Model):
|
||||
__tablename__ = 'users'
|
||||
|
@ -56,12 +56,12 @@ class User(DB.Model):
|
|||
return False
|
||||
|
||||
def get_id(self):
|
||||
return unicode(self.id)
|
||||
return self.id
|
||||
|
||||
def reset_password_digest(self):
|
||||
return hmac.new(
|
||||
settings.NONCE_SECRET,
|
||||
'id={0}&password={1}'.format(self.id, self.password),
|
||||
'id={0}&password={1}'.format(self.id, self.password).encode('utf-8'),
|
||||
hashlib.sha256
|
||||
).hexdigest()
|
||||
|
||||
|
@ -121,7 +121,9 @@ class Email(DB.Model):
|
|||
email=addr,
|
||||
user_id=user_id)
|
||||
digest = hmac.new(
|
||||
settings.NONCE_SECRET, message.encode('utf-8'), hashlib.sha256
|
||||
settings.NONCE_SECRET,
|
||||
message.encode('utf-8'),
|
||||
hashlib.sha256
|
||||
).hexdigest()
|
||||
link = url_for('confirm-account-email',
|
||||
digest=digest, email=addr, _external=True)
|
||||
|
@ -145,7 +147,9 @@ class Email(DB.Model):
|
|||
email=addr,
|
||||
user_id=user_id)
|
||||
what_should_be = hmac.new(
|
||||
settings.NONCE_SECRET, message.encode('utf-8'), hashlib.sha256
|
||||
settings.NONCE_SECRET,
|
||||
message.encode('utf-8'),
|
||||
hashlib.sha256
|
||||
).hexdigest()
|
||||
if digest == what_should_be:
|
||||
return cls(address=addr, owner_id=user_id)
|
||||
|
|
|
@ -5,11 +5,12 @@ from flask import request, flash, url_for, render_template, redirect, g
|
|||
from flask_login import login_user, logout_user, \
|
||||
current_user, login_required
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
from helpers import check_password, hash_pwd
|
||||
from formspree.app import DB, celery
|
||||
|
||||
from formspree import settings
|
||||
from models import User, Email
|
||||
from formspree.stuff import DB, celery
|
||||
from formspree.utils import send_email
|
||||
from .models import User, Email
|
||||
from .helpers import check_password, hash_pwd
|
||||
|
||||
|
||||
def register():
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import requests
|
||||
import datetime
|
||||
import calendar
|
||||
import urlparse
|
||||
from urllib.parse import urlparse, urlunparse
|
||||
import uuid
|
||||
import json
|
||||
import re
|
||||
|
@ -12,7 +12,7 @@ from formspree import settings
|
|||
IS_VALID_EMAIL = lambda x: re.match(r"[^@]+@[^@]+\.[^@]+", x)
|
||||
|
||||
def valid_url(url):
|
||||
parsed = urlparse.urlparse(url)
|
||||
parsed = urlparse(url)
|
||||
return len(parsed.scheme) > 0 and len(parsed.netloc) > 0 and not 'javascript:' in url
|
||||
|
||||
def request_wants_json():
|
||||
|
@ -75,14 +75,14 @@ def get_url(endpoint, secure=False, **values):
|
|||
|
||||
|
||||
def url_domain(url):
|
||||
parsed = urlparse.urlparse(url)
|
||||
parsed = urlparse(url)
|
||||
return '.'.join(parsed.netloc.split('.')[-2:])
|
||||
|
||||
|
||||
def unix_time_for_12_months_from_now(now=None):
|
||||
now = now or datetime.date.today()
|
||||
month = now.month - 1 + 12
|
||||
next_year = now.year + month / 12
|
||||
next_year = now.year + int(month / 12)
|
||||
next_month = month % 12 + 1
|
||||
start_of_next_month = datetime.datetime(next_year, next_month, 1, 0, 0)
|
||||
return calendar.timegm(start_of_next_month.utctimetuple())
|
||||
|
@ -102,10 +102,10 @@ def next_url(referrer=None, next=None):
|
|||
# parts from _next. so, if _next is only a path it will just use
|
||||
# that path. if it is a netloc without a scheme, will use that
|
||||
# netloc, but reuse the scheme from base and so on.
|
||||
parsed_next = urlparse.urlparse(next)
|
||||
base = urlparse.urlparse(referrer)
|
||||
parsed_next = urlparse(next)
|
||||
base = urlparse(referrer)
|
||||
|
||||
return urlparse.urlunparse([
|
||||
return urlunparse([
|
||||
parsed_next.scheme or base.scheme,
|
||||
parsed_next.netloc or base.netloc,
|
||||
parsed_next.path or base.path,
|
||||
|
|
104
manage.py
104
manage.py
|
@ -1,104 +0,0 @@
|
|||
import os
|
||||
import datetime
|
||||
import click
|
||||
|
||||
from flask_script import prompt_bool
|
||||
from flask_migrate import Migrate
|
||||
|
||||
from formspree import create_app, app, settings
|
||||
from formspree.app import redis_store
|
||||
from formspree.forms.helpers import REDIS_COUNTER_KEY
|
||||
from formspree.forms.models import Form
|
||||
|
||||
from celery.bin.celery import main as celery_main
|
||||
|
||||
forms_app = create_app()
|
||||
|
||||
# add flask-migrate commands
|
||||
migrate = Migrate(forms_app, app.DB)
|
||||
|
||||
@forms_app.cli.command()
|
||||
def run_debug(port=os.getenv('PORT', 5000)):
|
||||
'''runs the app with debug flag set to true'''
|
||||
forms_app.run(host='0.0.0.0', debug=True, port=int(port))
|
||||
|
||||
@forms_app.cli.command()
|
||||
@click.option('-H', '--host', default=None, help='referer hostname')
|
||||
@click.option('-e', '--email', default=None, help='form email')
|
||||
def unsubscribe(email, host):
|
||||
''' Unsubscribes an email by resetting the form to unconfirmed. User may get
|
||||
one more confirmation email, but if she doesn't confirm that will be it.'''
|
||||
|
||||
form = None
|
||||
|
||||
if email and host:
|
||||
form = Form.query.filter_by(email=email, host=host).first()
|
||||
elif email and not host:
|
||||
query = Form.query.filter_by(email=email)
|
||||
if query.count() == 1:
|
||||
form = query.first()
|
||||
elif query.count() > 1:
|
||||
for f in query.all():
|
||||
print '-', f.host
|
||||
print 'More than one result for this email, specify the host.'
|
||||
elif host and not email:
|
||||
query = Form.query.filter_by(host=host)
|
||||
if query.count() == 1:
|
||||
form = query.first()
|
||||
elif query.count() > 1:
|
||||
for f in query.all():
|
||||
print '-', f.email
|
||||
print 'More than one result for this host, specify the email.'
|
||||
|
||||
if form:
|
||||
print 'unsubscribing the email %s from the form at %s' % (form.email, form.host)
|
||||
if prompt_bool('are you sure?'):
|
||||
form.confirmed = False
|
||||
form.confirm_sent = False
|
||||
app.DB.session.add(form)
|
||||
app.DB.session.commit()
|
||||
print 'success.'
|
||||
|
||||
@forms_app.cli.command()
|
||||
@click.option('-i', '--id', default=None, help='form id')
|
||||
@click.option('-H', '--host', default=None, help='referer hostname')
|
||||
@click.option('-e', '--email', default=None, help='form email')
|
||||
def monthly_counters(email=None, host=None, id=None, month=datetime.date.today().month):
|
||||
if id:
|
||||
query = [Form.query.get(id)]
|
||||
elif email and host:
|
||||
query = Form.query.filter_by(email=email, host=host)
|
||||
elif email and not host:
|
||||
query = Form.query.filter_by(email=email)
|
||||
elif host and not email:
|
||||
query = Form.query.filter_by(host=host)
|
||||
else:
|
||||
print 'supply each --email or --form or both (or --id).'
|
||||
return 1
|
||||
|
||||
for form in query:
|
||||
nsubmissions = redis_store.get(REDIS_COUNTER_KEY(form_id=form.id, month=month)) or 0
|
||||
print '%s submissions for %s' % (nsubmissions, form)
|
||||
|
||||
|
||||
@forms_app.cli.command()
|
||||
@click.option('-t', '--testname', 'testname', default=None, help='name of test')
|
||||
def test(testname=None):
|
||||
import unittest
|
||||
|
||||
test_loader = unittest.defaultTestLoader
|
||||
if testname:
|
||||
test_suite = test_loader.loadTestsFromName(testname)
|
||||
else:
|
||||
test_suite = test_loader.discover('.')
|
||||
|
||||
test_runner = unittest.TextTestRunner()
|
||||
test_runner.run(test_suite)
|
||||
|
||||
@forms_app.cli.command()
|
||||
def celery():
|
||||
celery_args = ['celery', 'worker', '-B', '-s', '/tmp/celery.db', '--concurrency=5']
|
||||
return celery_main(celery_args)
|
||||
|
||||
if __name__ == "__main__":
|
||||
forms_app.run()
|
|
@ -1,36 +0,0 @@
|
|||
sqlalchemy==1.0.13
|
||||
alembic==0.8.10
|
||||
celery==4.1.0
|
||||
click==6.7
|
||||
fakeredis==0.8.2
|
||||
Flask==0.12
|
||||
Flask-CDN==1.5.3
|
||||
Flask-Cors==3.0.2
|
||||
Flask-Limiter==0.9.3
|
||||
Flask-Login==0.3.0
|
||||
Flask-Migrate==2.0.3
|
||||
Flask-Redis==0.0.6
|
||||
Flask-Script==2.0.5
|
||||
Flask-SQLAlchemy==2.1
|
||||
Flask-Testing==0.6.1
|
||||
funcsigs==1.0.2
|
||||
future==0.16.0
|
||||
gunicorn==19.6.0
|
||||
hashids==1.0.2
|
||||
httpretty==0.8.14
|
||||
itsdangerous==0.24
|
||||
git+https://github.com/mitsuhiko/jinja2@84f39ff5afd89760e1eff387ba4ce38e4a982977
|
||||
limits==1.2.1
|
||||
Mako==1.0.6
|
||||
MarkupSafe==0.23
|
||||
mock==2.0.0
|
||||
Paste==2.0.3
|
||||
pbr==1.10.0
|
||||
psycopg2==2.7
|
||||
python-dotenv==0.3.0
|
||||
redis==2.10.5
|
||||
requests==2.1.0
|
||||
stripe==1.55.0
|
||||
structlog==16.1.0
|
||||
unicodecsv==0.14.1
|
||||
pyaml==17.12.1
|
|
@ -1 +0,0 @@
|
|||
python-2.7.11
|
|
@ -2,12 +2,11 @@
|
|||
import os
|
||||
import redis
|
||||
import fakeredis
|
||||
|
||||
from flask_testing import TestCase
|
||||
import mock
|
||||
from formspree import create_app
|
||||
from formspree import settings
|
||||
from formspree.app import DB, redis_store
|
||||
|
||||
from formspree import create_app, settings
|
||||
from formspree.stuff import DB, redis_store
|
||||
|
||||
|
||||
# the different redis database only accessed by flask-limiter
|
||||
|
|
|
@ -2,12 +2,12 @@ import httpretty
|
|||
import json
|
||||
|
||||
from formspree import settings
|
||||
from formspree.app import DB
|
||||
from formspree.stuff import DB
|
||||
from formspree.forms.helpers import HASH
|
||||
from formspree.users.models import User
|
||||
from formspree.forms.models import Form, Submission
|
||||
|
||||
from formspree_test_case import FormspreeTestCase
|
||||
from .formspree_test_case import FormspreeTestCase
|
||||
|
||||
class ArchiveSubmissionsTestCase(FormspreeTestCase):
|
||||
@httpretty.activate
|
||||
|
@ -176,7 +176,7 @@ class ArchiveSubmissionsTestCase(FormspreeTestCase):
|
|||
'Content-type': 'application/json'},
|
||||
data=json.dumps({'email': 'hope@springs.com'})
|
||||
)
|
||||
resp = json.loads(r.data)
|
||||
resp = json.loads(r.data.decode('utf-8'))
|
||||
form_endpoint = resp['hashid']
|
||||
|
||||
# manually confirm the form
|
||||
|
@ -195,20 +195,20 @@ class ArchiveSubmissionsTestCase(FormspreeTestCase):
|
|||
r = self.client.get('/forms/' + form_endpoint + '/',
|
||||
headers={'Accept': 'application/json'}
|
||||
)
|
||||
submissions = json.loads(r.data)['submissions']
|
||||
submissions = json.loads(r.data.decode('utf-8'))['submissions']
|
||||
self.assertEqual(len(submissions), 1)
|
||||
self.assertEqual(submissions[0]['name'], 'bruce')
|
||||
self.assertEqual(submissions[0]['message'], 'hi, my name is bruce!')
|
||||
|
||||
# test exporting feature (both json and csv file downloads)
|
||||
r = self.client.get('/forms/' + form_endpoint + '.json')
|
||||
submissions = json.loads(r.data)['submissions']
|
||||
submissions = json.loads(r.data.decode('utf-8'))['submissions']
|
||||
self.assertEqual(len(submissions), 1)
|
||||
self.assertEqual(submissions[0]['name'], 'bruce')
|
||||
self.assertEqual(submissions[0]['message'], 'hi, my name is bruce!')
|
||||
|
||||
r = self.client.get('/forms/' + form_endpoint + '.csv')
|
||||
lines = r.data.splitlines()
|
||||
lines = r.data.decode('utf-8').splitlines()
|
||||
self.assertEqual(len(lines), 2)
|
||||
self.assertEqual(lines[0], 'date,message,name')
|
||||
self.assertIn('"hi, my name is bruce!"', lines[1])
|
||||
|
|
|
@ -3,9 +3,9 @@ import json
|
|||
|
||||
from formspree.forms.models import Form
|
||||
from formspree import settings
|
||||
from formspree.app import DB
|
||||
from formspree.stuff import DB
|
||||
|
||||
from formspree_test_case import FormspreeTestCase
|
||||
from .formspree_test_case import FormspreeTestCase
|
||||
|
||||
class ContentTypeTestCase(FormspreeTestCase):
|
||||
|
||||
|
@ -24,16 +24,16 @@ class ContentTypeTestCase(FormspreeTestCase):
|
|||
|
||||
def isjson(res):
|
||||
try:
|
||||
d = json.loads(res.get_data())
|
||||
d = json.loads(res.data.decode('utf-8'))
|
||||
self.assertIsInstance(d, dict)
|
||||
self.assertIn('success', d)
|
||||
self.assertEqual(res.mimetype, 'application/json')
|
||||
except ValueError, e:
|
||||
except ValueError as e:
|
||||
self.assertFalse(e)
|
||||
|
||||
def ishtml(res):
|
||||
try:
|
||||
d = json.loads(res.get_data())
|
||||
d = json.loads(res.data.decode('utf-8'))
|
||||
self.assertNotIsInstance(d, dict)
|
||||
except ValueError:
|
||||
self.assertEqual(res.mimetype, 'text/html')
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
# encoding: utf-8
|
||||
|
||||
import httpretty
|
||||
import json
|
||||
|
||||
from formspree import settings
|
||||
from formspree.app import DB
|
||||
from formspree.stuff import DB
|
||||
from formspree.users.models import User, Email
|
||||
from formspree.forms.models import Form
|
||||
|
||||
from formspree_test_case import FormspreeTestCase
|
||||
from utils import parse_confirmation_link_sent
|
||||
from .formspree_test_case import FormspreeTestCase
|
||||
from .utils import parse_confirmation_link_sent
|
||||
|
||||
class EmailConfirmationsTestCase(FormspreeTestCase):
|
||||
|
||||
|
@ -32,7 +30,7 @@ class EmailConfirmationsTestCase(FormspreeTestCase):
|
|||
for i, addr in enumerate(emails):
|
||||
self.client.post('/account/add-email', data={'address': addr})
|
||||
|
||||
link, qs = parse_confirmation_link_sent(httpretty.last_request().body)
|
||||
link, qs = parse_confirmation_link_sent(httpretty.last_request().body.decode('utf-8'))
|
||||
self.client.get(link, query_string=qs)
|
||||
|
||||
email = Email.query.get([addr, user.id])
|
||||
|
@ -65,18 +63,18 @@ class EmailConfirmationsTestCase(FormspreeTestCase):
|
|||
r = self.client.get('/forms',
|
||||
headers={'Accept': 'application/json'},
|
||||
)
|
||||
forms = json.loads(r.data)['forms']
|
||||
forms = json.loads(r.data.decode('utf-8'))['forms']
|
||||
self.assertEqual(0, len(forms))
|
||||
|
||||
# verify user email
|
||||
link, qs = parse_confirmation_link_sent(httpretty.last_request().body)
|
||||
link, qs = parse_confirmation_link_sent(httpretty.last_request().body.decode('utf-8'))
|
||||
self.client.get(link, query_string=qs)
|
||||
|
||||
# confirm that the user has no access to the form since he is not upgraded
|
||||
r = self.client.get('/forms',
|
||||
headers={'Accept': 'application/json'},
|
||||
)
|
||||
forms = json.loads(r.data)['forms']
|
||||
forms = json.loads(r.data.decode('utf-8'))['forms']
|
||||
self.assertEqual(0, len(forms))
|
||||
|
||||
# upgrade user
|
||||
|
@ -89,7 +87,7 @@ class EmailConfirmationsTestCase(FormspreeTestCase):
|
|||
r = self.client.get('/forms',
|
||||
headers={'Accept': 'application/json'},
|
||||
)
|
||||
forms = json.loads(r.data)['forms']
|
||||
forms = json.loads(r.data.decode('utf-8'))['forms']
|
||||
self.assertEqual(1, len(forms))
|
||||
self.assertEqual(forms[0]['email'], u'márkö@example.com')
|
||||
self.assertEqual(forms[0]['host'], 'tomatoes.com')
|
||||
|
@ -109,20 +107,20 @@ class EmailConfirmationsTestCase(FormspreeTestCase):
|
|||
r = self.client.get('/forms',
|
||||
headers={'Accept': 'application/json'},
|
||||
)
|
||||
forms = json.loads(r.data)['forms']
|
||||
forms = json.loads(r.data.decode('utf-8'))['forms']
|
||||
self.assertEqual(1, len(forms))
|
||||
|
||||
# add this other email address to user account
|
||||
self.client.post('/account/add-email', data={'address': 'contact@mark.com'})
|
||||
|
||||
link, qs = parse_confirmation_link_sent(httpretty.last_request().body)
|
||||
link, qs = parse_confirmation_link_sent(httpretty.last_request().body.decode('utf-8'))
|
||||
self.client.get(link, query_string=qs)
|
||||
|
||||
# confirm that the user account now has access to the form
|
||||
r = self.client.get('/forms',
|
||||
headers={'Accept': 'application/json'},
|
||||
)
|
||||
forms = json.loads(r.data)['forms']
|
||||
forms = json.loads(r.data.decode('utf-8'))['forms']
|
||||
self.assertEqual(2, len(forms))
|
||||
self.assertEqual(forms[0]['email'], 'contact@mark.com') # forms are sorted by -id, so the newer comes first
|
||||
self.assertEqual(forms[0]['host'], 'mark.com')
|
||||
|
@ -142,7 +140,7 @@ class EmailConfirmationsTestCase(FormspreeTestCase):
|
|||
r = self.client.get('/forms',
|
||||
headers={'Accept': 'application/json'},
|
||||
)
|
||||
forms = json.loads(r.data)['forms']
|
||||
forms = json.loads(r.data.decode('utf-8'))['forms']
|
||||
self.assertEqual(3, len(forms))
|
||||
self.assertEqual(forms[0]['email'], u'márkö@example.com')
|
||||
self.assertEqual(forms[0]['host'], 'elsewhere.com')
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
# encoding: utf-8
|
||||
|
||||
import httpretty
|
||||
import json
|
||||
|
||||
from formspree import settings
|
||||
from formspree.app import DB
|
||||
from formspree.stuff import DB
|
||||
from formspree.forms.helpers import HASH
|
||||
from formspree.users.models import User, Email
|
||||
from formspree.forms.models import Form, Submission
|
||||
|
||||
from formspree_test_case import FormspreeTestCase
|
||||
from utils import parse_confirmation_link_sent
|
||||
from .formspree_test_case import FormspreeTestCase
|
||||
from .utils import parse_confirmation_link_sent
|
||||
|
||||
class TestFormCreationFromDashboard(FormspreeTestCase):
|
||||
|
||||
|
@ -32,7 +30,7 @@ class TestFormCreationFromDashboard(FormspreeTestCase):
|
|||
data={'email': 'hope@springs.com'}
|
||||
)
|
||||
self.assertEqual(r.status_code, 402)
|
||||
self.assertIn('error', json.loads(r.data))
|
||||
self.assertIn('error', json.loads(r.data.decode('utf-8')))
|
||||
self.assertEqual(0, Form.query.count())
|
||||
|
||||
# upgrade user manually
|
||||
|
@ -46,7 +44,7 @@ class TestFormCreationFromDashboard(FormspreeTestCase):
|
|||
headers={'Accept': 'application/json', 'Content-type': 'application/json'},
|
||||
data=json.dumps({'email': 'hope@springs.com'})
|
||||
)
|
||||
resp = json.loads(r.data)
|
||||
resp = json.loads(r.data.decode('utf-8'))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertIn('submission_url', resp)
|
||||
self.assertIn('hashid', resp)
|
||||
|
@ -60,8 +58,8 @@ class TestFormCreationFromDashboard(FormspreeTestCase):
|
|||
headers={'Referer': 'http://testsite.com'},
|
||||
data={'name': 'bruce'}
|
||||
)
|
||||
self.assertIn("sent an email confirmation", r.data)
|
||||
self.assertIn('confirm+your+email', httpretty.last_request().body)
|
||||
self.assertIn("sent an email confirmation", r.data.decode('utf-8'))
|
||||
self.assertIn('confirm+your+email', httpretty.last_request().body.decode('utf-8'))
|
||||
self.assertEqual(1, Form.query.count())
|
||||
|
||||
# confirm form
|
||||
|
@ -83,9 +81,9 @@ class TestFormCreationFromDashboard(FormspreeTestCase):
|
|||
form = Form.query.first()
|
||||
self.assertEqual(form.counter, 5)
|
||||
self.assertEqual(form.get_monthly_counter(), 5)
|
||||
self.assertIn('ana', httpretty.last_request().body)
|
||||
self.assertIn('__4__', httpretty.last_request().body)
|
||||
self.assertNotIn('You+are+past+our+limit', httpretty.last_request().body)
|
||||
self.assertIn('ana', httpretty.last_request().body.decode('utf-8'))
|
||||
self.assertIn('__4__', httpretty.last_request().body.decode('utf-8'))
|
||||
self.assertNotIn('You+are+past+our+limit', httpretty.last_request().body.decode('utf-8'))
|
||||
|
||||
# try (and fail) to submit from a different host
|
||||
r = self.client.post('/' + form_endpoint,
|
||||
|
@ -93,8 +91,8 @@ class TestFormCreationFromDashboard(FormspreeTestCase):
|
|||
data={'name': 'usurper'}
|
||||
)
|
||||
self.assertEqual(r.status_code, 403)
|
||||
self.assertIn('ana', httpretty.last_request().body) # no more data is sent to sendgrid
|
||||
self.assertIn('__4__', httpretty.last_request().body)
|
||||
self.assertIn('ana', httpretty.last_request().body.decode('utf-8')) # no more data is sent to sendgrid
|
||||
self.assertIn('__4__', httpretty.last_request().body.decode('utf-8'))
|
||||
|
||||
@httpretty.activate
|
||||
def test_form_creation_with_a_registered_email(self):
|
||||
|
@ -127,11 +125,11 @@ class TestFormCreationFromDashboard(FormspreeTestCase):
|
|||
data=json.dumps({'email': 'email@testsite.com',
|
||||
'url': 'https://www.testsite.com/contact.html'})
|
||||
)
|
||||
resp = json.loads(r.data)
|
||||
resp = json.loads(r.data.decode('utf-8'))
|
||||
self.assertEqual(resp['confirmed'], False)
|
||||
self.assertEqual(httpretty.has_request(), True)
|
||||
self.assertIn('Confirm+email', httpretty.last_request().body)
|
||||
self.assertIn('www.testsite.com%2Fcontact.html', httpretty.last_request().body)
|
||||
self.assertIn('Confirm+email', httpretty.last_request().body.decode('utf-8'))
|
||||
self.assertIn('www.testsite.com%2Fcontact.html', httpretty.last_request().body.decode('utf-8'))
|
||||
|
||||
# manually verify an email
|
||||
email = Email()
|
||||
|
@ -146,15 +144,16 @@ class TestFormCreationFromDashboard(FormspreeTestCase):
|
|||
data=json.dumps({'email': 'owned-by@testsite.com',
|
||||
'url': 'https://www.testsite.com/about.html'})
|
||||
)
|
||||
resp = json.loads(r.data)
|
||||
resp = json.loads(r.data.decode('utf-8'))
|
||||
self.assertEqual(resp['confirmed'], True)
|
||||
self.assertIn('www.testsite.com%2Fcontact.html', httpretty.last_request().body) # same as the last, means no new request was made
|
||||
self.assertIn('www.testsite.com%2Fcontact.html', httpretty.last_request().body.decode('utf-8')) # same as the last, means no new request was made
|
||||
|
||||
# should have three created forms in the end
|
||||
self.assertEqual(Form.query.count(), 3)
|
||||
|
||||
@httpretty.activate
|
||||
def test_sitewide_forms(self):
|
||||
httpretty.register_uri(httpretty.POST, 'https://api.sendgrid.com/api/mail.send.json')
|
||||
httpretty.register_uri(httpretty.GET,
|
||||
'http://mysite.com/formspree-verify.txt',
|
||||
body=u'other_email@forms.com\nmyüñìćõð€email@email.com',
|
||||
|
@ -189,7 +188,7 @@ class TestFormCreationFromDashboard(FormspreeTestCase):
|
|||
'url': 'http://mysite.com',
|
||||
'sitewide': 'true'})
|
||||
)
|
||||
resp = json.loads(r.data)
|
||||
resp = json.loads(r.data.decode('utf-8'))
|
||||
|
||||
self.assertEqual(httpretty.has_request(), True)
|
||||
self.assertEqual(resp['confirmed'], True)
|
||||
|
@ -207,19 +206,19 @@ class TestFormCreationFromDashboard(FormspreeTestCase):
|
|||
headers = {'Referer': 'http://www.mysite.com/hipopotamo', 'content-type': 'application/json'},
|
||||
data=json.dumps({'name': 'alice'})
|
||||
)
|
||||
self.assertIn('alice', httpretty.last_request().body)
|
||||
self.assertIn('alice', httpretty.last_request().body.decode('utf-8'))
|
||||
|
||||
self.client.post('/' + form.hashid,
|
||||
headers = {'Referer': 'http://mysite.com/baleia/urso?w=2', 'content-type': 'application/json'},
|
||||
data=json.dumps({'name': 'maria'})
|
||||
)
|
||||
self.assertIn('maria', httpretty.last_request().body)
|
||||
self.assertIn('maria', httpretty.last_request().body.decode('utf-8'))
|
||||
|
||||
self.client.post('/' + form.hashid,
|
||||
headers = {'Referer': 'http://mysite.com/', 'content-type': 'application/json'},
|
||||
data=json.dumps({'name': 'laura'})
|
||||
)
|
||||
self.assertIn('laura', httpretty.last_request().body)
|
||||
self.assertIn('laura', httpretty.last_request().body.decode('utf-8'))
|
||||
|
||||
# another form, now with a www prefix that will be stripped
|
||||
r = self.client.post('/forms',
|
||||
|
@ -228,7 +227,7 @@ class TestFormCreationFromDashboard(FormspreeTestCase):
|
|||
'url': 'http://www.naive.com',
|
||||
'sitewide': 'true'})
|
||||
)
|
||||
resp = json.loads(r.data)
|
||||
resp = json.loads(r.data.decode('utf-8'))
|
||||
|
||||
self.assertEqual(httpretty.has_request(), True)
|
||||
self.assertEqual(resp['confirmed'], True)
|
||||
|
@ -246,19 +245,19 @@ class TestFormCreationFromDashboard(FormspreeTestCase):
|
|||
headers={'Referer': 'http://naive.com/hipopotamo', 'content-type': 'application/json'},
|
||||
data=json.dumps({'name': 'alice'})
|
||||
)
|
||||
self.assertIn('alice', httpretty.last_request().body)
|
||||
self.assertIn('alice', httpretty.last_request().body.decode('utf-8'))
|
||||
|
||||
self.client.post('/' + form.hashid,
|
||||
headers={'Referer': 'http://www.naive.com/baleia/urso?w=2', 'content-type': 'application/json'},
|
||||
data=json.dumps({'name': 'maria'})
|
||||
)
|
||||
self.assertIn('maria', httpretty.last_request().body)
|
||||
self.assertIn('maria', httpretty.last_request().body.decode('utf-8'))
|
||||
|
||||
self.client.post('/' + form.hashid,
|
||||
headers={'Referer': 'http://www.naive.com/', 'content-type': 'application/json'},
|
||||
data=json.dumps({'name': 'laura'})
|
||||
)
|
||||
self.assertIn('laura', httpretty.last_request().body)
|
||||
self.assertIn('laura', httpretty.last_request().body.decode('utf-8'))
|
||||
|
||||
# create a different form with the same email address, now using unprefixed url
|
||||
r = self.client.post('/forms',
|
||||
|
@ -267,7 +266,7 @@ class TestFormCreationFromDashboard(FormspreeTestCase):
|
|||
'url': 'mysite.com',
|
||||
'sitewide': 'true'})
|
||||
)
|
||||
resp = json.loads(r.data)
|
||||
resp = json.loads(r.data.decode('utf-8'))
|
||||
|
||||
@httpretty.activate
|
||||
def test_form_settings(self):
|
||||
|
@ -288,7 +287,7 @@ class TestFormCreationFromDashboard(FormspreeTestCase):
|
|||
headers={'Accept': 'application/json', 'Content-type': 'application/json'},
|
||||
data=json.dumps({'email': 'texas@springs.com'})
|
||||
)
|
||||
resp = json.loads(r.data)
|
||||
resp = json.loads(r.data.decode('utf-8'))
|
||||
form = Form.query.first()
|
||||
form.confirmed = True
|
||||
DB.session.add(form)
|
||||
|
@ -308,7 +307,10 @@ class TestFormCreationFromDashboard(FormspreeTestCase):
|
|||
data={'name': 'bruce'}
|
||||
)
|
||||
# make sure it doesn't send the email
|
||||
self.assertNotIn('Someone+just+submitted+your+form', httpretty.last_request().body)
|
||||
self.assertNotIn(
|
||||
'Someone+just+submitted+your+form',
|
||||
httpretty.last_request().body.decode('utf-8')
|
||||
)
|
||||
|
||||
# disable archive storage on this form
|
||||
self.client.post('/forms/' + form_endpoint + '/toggle-storage',
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import httpretty
|
||||
|
||||
from formspree import settings
|
||||
from formspree.app import DB
|
||||
from formspree.stuff import DB
|
||||
from formspree.forms.models import Form
|
||||
from formspree.users.models import User, Email
|
||||
|
||||
from formspree_test_case import FormspreeTestCase
|
||||
from .formspree_test_case import FormspreeTestCase
|
||||
|
||||
http_headers = {
|
||||
'Referer': 'testwebsite.com'
|
||||
|
@ -46,10 +46,10 @@ class FormPostsTestCase(FormspreeTestCase):
|
|||
'_subject': 'my-nice-subject',
|
||||
'_format': 'plain'}
|
||||
)
|
||||
self.assertIn('my-nice-subject', httpretty.last_request().body)
|
||||
self.assertNotIn('_subject', httpretty.last_request().body)
|
||||
self.assertNotIn('_format', httpretty.last_request().body)
|
||||
self.assertNotIn('plain', httpretty.last_request().body)
|
||||
self.assertIn('my-nice-subject', httpretty.last_request().body.decode('utf-8'))
|
||||
self.assertNotIn('_subject', httpretty.last_request().body.decode('utf-8'))
|
||||
self.assertNotIn('_format', httpretty.last_request().body.decode('utf-8'))
|
||||
self.assertNotIn('plain', httpretty.last_request().body.decode('utf-8'))
|
||||
|
||||
@httpretty.activate
|
||||
def test_fail_form_without_header(self):
|
||||
|
@ -73,7 +73,7 @@ class FormPostsTestCase(FormspreeTestCase):
|
|||
headers={'Referer': settings.SERVICE_URL},
|
||||
data={'name': 'alice', '_subject': 'my-nice-subject'}
|
||||
)
|
||||
self.assertIn("Unable to submit form", r.data)
|
||||
self.assertIn("Unable to submit form", r.data.decode('utf-8'))
|
||||
self.assertNotEqual(200, r.status_code)
|
||||
self.assertFalse(httpretty.has_request())
|
||||
self.assertEqual(0, Form.query.count())
|
||||
|
@ -231,7 +231,7 @@ class FormPostsTestCase(FormspreeTestCase):
|
|||
data={'name': 'peter'}
|
||||
)
|
||||
self.assertEqual(r.status_code, 302)
|
||||
self.assertIn('peter', httpretty.last_request().body)
|
||||
self.assertIn('peter', httpretty.last_request().body.decode('utf-8'))
|
||||
|
||||
# second submission
|
||||
httpretty.register_uri(httpretty.POST, 'https://api.sendgrid.com/api/mail.send.json')
|
||||
|
@ -240,7 +240,7 @@ class FormPostsTestCase(FormspreeTestCase):
|
|||
data={'name': 'ana'}
|
||||
)
|
||||
self.assertEqual(r.status_code, 302)
|
||||
self.assertIn('ana', httpretty.last_request().body)
|
||||
self.assertIn('ana', httpretty.last_request().body.decode('utf-8'))
|
||||
|
||||
# third submission, now we're over the limit
|
||||
httpretty.register_uri(httpretty.POST, 'https://api.sendgrid.com/api/mail.send.json')
|
||||
|
@ -252,8 +252,8 @@ class FormPostsTestCase(FormspreeTestCase):
|
|||
# being the form over the limits or not
|
||||
|
||||
# but the mocked sendgrid should never receive this last form
|
||||
self.assertNotIn('maria', httpretty.last_request().body)
|
||||
self.assertIn('You+are+past+our+limit', httpretty.last_request().body)
|
||||
self.assertNotIn('maria', httpretty.last_request().body.decode('utf-8'))
|
||||
self.assertIn('You+are+past+our+limit', httpretty.last_request().body.decode('utf-8'))
|
||||
|
||||
# all the other variables are ok:
|
||||
self.assertEqual(1, Form.query.count())
|
||||
|
@ -279,7 +279,7 @@ class FormPostsTestCase(FormspreeTestCase):
|
|||
data={'name': 'noah'}
|
||||
)
|
||||
self.assertEqual(r.status_code, 302)
|
||||
self.assertIn('noah', httpretty.last_request().body)
|
||||
self.assertIn('noah', httpretty.last_request().body.decode('utf-8'))
|
||||
|
||||
@httpretty.activate
|
||||
def test_first_submission_is_stored(self):
|
||||
|
@ -295,7 +295,7 @@ class FormPostsTestCase(FormspreeTestCase):
|
|||
self.assertEqual(f.get_monthly_counter(), 0) # monthly submissions also 0
|
||||
|
||||
# got a confirmation email
|
||||
self.assertIn('one+step+away', httpretty.last_request().body)
|
||||
self.assertIn('one+step+away', httpretty.last_request().body.decode('utf-8'))
|
||||
|
||||
# clicking of activation link
|
||||
self.client.get('/confirm/%s' % (f.hash,))
|
||||
|
@ -306,4 +306,4 @@ class FormPostsTestCase(FormspreeTestCase):
|
|||
self.assertEqual(f.get_monthly_counter(), 1) # monthly submissions also
|
||||
|
||||
# got the first (missed) submission
|
||||
self.assertIn('this+was+important', httpretty.last_request().body)
|
||||
self.assertIn('this+was+important', httpretty.last_request().body.decode('utf-8'))
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import httpretty
|
||||
import urllib2
|
||||
import json
|
||||
import re
|
||||
from urllib.parse import unquote
|
||||
|
||||
from formspree.forms.models import Form
|
||||
from formspree import settings
|
||||
from formspree.app import DB
|
||||
from formspree.stuff import DB
|
||||
|
||||
from formspree_test_case import FormspreeTestCase
|
||||
from .formspree_test_case import FormspreeTestCase
|
||||
|
||||
class ListUnsubscribeTestCase(FormspreeTestCase):
|
||||
@httpretty.activate
|
||||
|
@ -21,7 +21,7 @@ class ListUnsubscribeTestCase(FormspreeTestCase):
|
|||
f = Form.query.first()
|
||||
|
||||
# List-Unsubscribe is present on confirmation email
|
||||
body = urllib2.unquote(httpretty.last_request().body)
|
||||
body = unquote(httpretty.last_request().body)
|
||||
res = re.search('"List-Unsubscribe":[^"]*"<([^>]+)>"', body)
|
||||
self.assertTrue(res is not None)
|
||||
list_unsubscribe_url = res.group(1)
|
||||
|
@ -38,7 +38,7 @@ class ListUnsubscribeTestCase(FormspreeTestCase):
|
|||
self.assertEqual(r.status_code, 302)
|
||||
|
||||
# List-Unsubscribe is present on normal submission
|
||||
body = urllib2.unquote(httpretty.last_request().body)
|
||||
body = unquote(httpretty.last_request().body)
|
||||
res = re.search('"List-Unsubscribe":[^"]*"<([^>]+)>"', body)
|
||||
self.assertTrue(res is not None)
|
||||
url = res.group(1)
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import httpretty
|
||||
|
||||
from formspree import settings
|
||||
from formspree.app import DB
|
||||
from formspree.stuff import DB
|
||||
from formspree.forms.models import Form
|
||||
from formspree_test_case import FormspreeTestCase
|
||||
|
||||
from .formspree_test_case import FormspreeTestCase
|
||||
|
||||
|
||||
class RateLimitingTestCase(FormspreeTestCase):
|
||||
|
|
|
@ -3,13 +3,13 @@ import json
|
|||
import stripe
|
||||
|
||||
from formspree import settings
|
||||
from formspree.app import DB
|
||||
from formspree.stuff import DB
|
||||
from formspree.forms.helpers import HASH
|
||||
from formspree.users.models import User, Email
|
||||
from formspree.forms.models import Form, Submission
|
||||
|
||||
from formspree_test_case import FormspreeTestCase
|
||||
from utils import parse_confirmation_link_sent
|
||||
from .formspree_test_case import FormspreeTestCase
|
||||
from .utils import parse_confirmation_link_sent
|
||||
|
||||
class UserAccountsTestCase(FormspreeTestCase):
|
||||
|
||||
|
@ -128,7 +128,7 @@ class UserAccountsTestCase(FormspreeTestCase):
|
|||
data={'email': 'hope@springs.com'}
|
||||
)
|
||||
self.assertEqual(r.status_code, 402)
|
||||
self.assertIn('error', json.loads(r.data))
|
||||
self.assertIn('error', json.loads(r.data.decode('utf-8')))
|
||||
self.assertEqual(0, Form.query.count())
|
||||
|
||||
# upgrade user manually
|
||||
|
@ -142,7 +142,7 @@ class UserAccountsTestCase(FormspreeTestCase):
|
|||
headers={'Accept': 'application/json', 'Content-type': 'application/json'},
|
||||
data=json.dumps({'email': 'hope@springs.com'})
|
||||
)
|
||||
resp = json.loads(r.data)
|
||||
resp = json.loads(r.data.decode('utf-8'))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertIn('submission_url', resp)
|
||||
self.assertIn('hashid', resp)
|
||||
|
@ -156,7 +156,7 @@ class UserAccountsTestCase(FormspreeTestCase):
|
|||
headers={'Referer': 'formspree.io'},
|
||||
data={'name': 'bruce'}
|
||||
)
|
||||
self.assertIn("We've sent a link to your email", r.data)
|
||||
self.assertIn("We've sent a link to your email", r.data.decode('utf-8'))
|
||||
self.assertIn('confirm+your+email', httpretty.last_request().body)
|
||||
self.assertEqual(1, Form.query.count())
|
||||
|
||||
|
@ -209,7 +209,7 @@ class UserAccountsTestCase(FormspreeTestCase):
|
|||
headers={'Accept': 'application/json', 'Content-type': 'application/json'},
|
||||
data=json.dumps({'email': 'hope@springs.com'})
|
||||
)
|
||||
resp = json.loads(r.data)
|
||||
resp = json.loads(r.data.decode('utf-8'))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertIn('submission_url', resp)
|
||||
self.assertIn('hashid', resp)
|
||||
|
@ -292,7 +292,7 @@ class UserAccountsTestCase(FormspreeTestCase):
|
|||
headers={'Accept': 'application/json', 'Content-type': 'application/json'},
|
||||
data=json.dumps({'email': 'hope@springs.com'})
|
||||
)
|
||||
resp = json.loads(r.data)
|
||||
resp = json.loads(r.data.decode('utf-8'))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertIn('submission_url', resp)
|
||||
self.assertIn('hashid', resp)
|
||||
|
@ -409,7 +409,10 @@ class UserAccountsTestCase(FormspreeTestCase):
|
|||
|
||||
# redirect back to /account, the HTML shows that the user is not yet
|
||||
# in the free plan, since it will be valid for the next 30 days
|
||||
self.assertIn("You've cancelled your subscription and it is ending on", r.data)
|
||||
self.assertIn(
|
||||
"You've cancelled your subscription and it is ending on",
|
||||
r.data.decode('utf-8')
|
||||
)
|
||||
|
||||
user = User.query.filter_by(email='maria@example.com').first()
|
||||
self.assertEqual(user.upgraded, True)
|
||||
|
@ -492,7 +495,10 @@ class UserAccountsTestCase(FormspreeTestCase):
|
|||
r = self.client.post('/card/add', data={
|
||||
'stripeToken': token
|
||||
}, follow_redirects=True)
|
||||
self.assertIn('That card already exists in your wallet', r.data)
|
||||
self.assertIn(
|
||||
'That card already exists in your wallet',
|
||||
r.data.decode('utf-8')
|
||||
)
|
||||
|
||||
# delete a card
|
||||
r = self.client.post('/card/%s/delete' % cards[1].id)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from formspree_test_case import FormspreeTestCase
|
||||
from .formspree_test_case import FormspreeTestCase
|
||||
|
||||
from formspree.utils import next_url
|
||||
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import re
|
||||
from urllib import unquote
|
||||
from urllib.parse import unquote
|
||||
|
||||
def parse_confirmation_link_sent(request_body):
|
||||
if type(request_body) != str:
|
||||
request_body = request_body.decode('utf-8')
|
||||
|
||||
request_body = unquote(request_body)
|
||||
matchlink = re.search('Link:\+([^?]+)\?(\S+)', request_body)
|
||||
if not matchlink:
|
||||
|
|
Loading…
Reference in New Issue