Browse Source

Initial commit

JoostSijm 6 years ago
commit
abf13134f7
11 changed files with 982 additions and 0 deletions
  1. 12 0
      .gitignore
  2. 201 0
      LICENSE
  3. 20 0
      Pipfile
  4. 310 0
      Pipfile.lock
  5. 53 0
      app/__init__.py
  6. 111 0
      app/flaskr.py
  7. 57 0
      app/models.py
  8. 9 0
      example.env
  9. 1 0
      flask.wsgi
  10. 164 0
      gulpfile.js
  11. 44 0
      package.json

+ 12 - 0
.gitignore

@@ -0,0 +1,12 @@
+*.pyc
+*.swp
+*.min.js
+*.min.css
+*.compiled.css
+node_modules/
+vendor/
+__pycache__/
+.venv/
+nohup.out
+yarn-error.log
+.env

+ 201 - 0
LICENSE

@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 20 - 0
Pipfile

@@ -0,0 +1,20 @@
+[[source]]
+url = "https://pypi.python.org/simple"
+verify_ssl = true
+name = "pypi"
+
+[packages]
+flask = "*"
+flask-menu = "*"
+flask-login = "*"
+flask-compress = "*"
+flask-argon2 = "*"
+flask-migrate = "*"
+flask-sqlalchemy = "*"
+flask-apscheduler = "*"
+apscheduler = "*"
+sqlalchemy = "*"
+psycopg2-binary = "*"
+github-webhook = "*"
+
+[dev-packages]

+ 310 - 0
Pipfile.lock

@@ -0,0 +1,310 @@
+{
+    "_meta": {
+        "hash": {
+            "sha256": "425023fabc1b9980845c7a3bf26b5a50d92007f73cfdc3f4e7bc31e663947895"
+        },
+        "pipfile-spec": 6,
+        "requires": {},
+        "sources": [
+            {
+                "name": "pypi",
+                "url": "https://pypi.python.org/simple",
+                "verify_ssl": true
+            }
+        ]
+    },
+    "default": {
+        "alembic": {
+            "hashes": [
+                "sha256:e9ffdece0eece55f4108b14b6b0f29ffc730d58e28446a434fe41a1cc5c5f266"
+            ],
+            "version": "==1.0.5"
+        },
+        "apscheduler": {
+            "hashes": [
+                "sha256:6599bc78901ee7e9be85cbd073d9cc155c42d2bc867c5cde4d4d1cc339ebfbeb",
+                "sha256:a8fe0c82d1c21bcf4a1b0e00aa35709f1f63fdd36446e406fa56cc0d51d3acc6"
+            ],
+            "index": "pypi",
+            "version": "==3.5.3"
+        },
+        "argon2-cffi": {
+            "hashes": [
+                "sha256:003f588de43a817af6ecc1c06103fa0801de63849db3cb0f37576bb2da29043d",
+                "sha256:04528ebbcc5d77eb49e7c2560fcf9d489cdc3b14f89fdd975c72c0a12934025a",
+                "sha256:04ead34244af38d79742cc46a212fec94daf99b49add66878f5d4b22da72d4aa",
+                "sha256:0529aeb71b50e068d300c992f850387c2456f2d3d4083d17d18e75710d057682",
+                "sha256:0d18a3dcb4ca7f3717155994a4f131a43072e47b708e57c4be16f60253337dfd",
+                "sha256:0ecbd2346da3e5af84427fd8df3ece484c903a9dafd9470571def47df54f2780",
+                "sha256:193de795483b00d752d16ec5df11d119a3a2c43f5464edfaf919a2ca9cc5b991",
+                "sha256:22a99f90da7176ee86fbdfb0a95411bc807b9d795b89495ee88c2e0468a496e8",
+                "sha256:2829d648dfa4d42ce33ec0f36e863d1068fd729b38ef6f830262b43e04f9ba1c",
+                "sha256:3b61a4ef1eb785d41f190520db716aa598d15f147419cbbdc9061dc232126f09",
+                "sha256:3ddcdde047cd4dba2bcce7d890dcefd6723548b849fa82ba87e04a468079b9b1",
+                "sha256:457c5db9bb99f2ffb7ce9ebf923b523898e75464dd019fbebdd1c6096ddcf044",
+                "sha256:51d78eedbba1f9e45a1c3fb1470ad6d1faafc6ec42eabb969df29c2aa848b645",
+                "sha256:af0d3dbc8f32d95be480eedd5d77fe8714f5441a28b9abcfa687ecf5301a1abd",
+                "sha256:ca65f736d2129687008178e3d9956264fd2be2f69429edf0d755c2f97cd003f1",
+                "sha256:d371fcd42e01c78c76397120d07c67f6e16f5fef97d327ad372c8debe38f9f56",
+                "sha256:ec12248d4c1e045a736beebf55daf1430c45a29ab8d773d8540c224555784275"
+            ],
+            "version": "==18.3.0"
+        },
+        "cffi": {
+            "hashes": [
+                "sha256:151b7eefd035c56b2b2e1eb9963c90c6302dc15fbd8c1c0a83a163ff2c7d7743",
+                "sha256:1553d1e99f035ace1c0544050622b7bc963374a00c467edafac50ad7bd276aef",
+                "sha256:1b0493c091a1898f1136e3f4f991a784437fac3673780ff9de3bcf46c80b6b50",
+                "sha256:2ba8a45822b7aee805ab49abfe7eec16b90587f7f26df20c71dd89e45a97076f",
+                "sha256:3bb6bd7266598f318063e584378b8e27c67de998a43362e8fce664c54ee52d30",
+                "sha256:3c85641778460581c42924384f5e68076d724ceac0f267d66c757f7535069c93",
+                "sha256:3eb6434197633b7748cea30bf0ba9f66727cdce45117a712b29a443943733257",
+                "sha256:495c5c2d43bf6cebe0178eb3e88f9c4aa48d8934aa6e3cddb865c058da76756b",
+                "sha256:4c91af6e967c2015729d3e69c2e51d92f9898c330d6a851bf8f121236f3defd3",
+                "sha256:57b2533356cb2d8fac1555815929f7f5f14d68ac77b085d2326b571310f34f6e",
+                "sha256:770f3782b31f50b68627e22f91cb182c48c47c02eb405fd689472aa7b7aa16dc",
+                "sha256:79f9b6f7c46ae1f8ded75f68cf8ad50e5729ed4d590c74840471fc2823457d04",
+                "sha256:7a33145e04d44ce95bcd71e522b478d282ad0eafaf34fe1ec5bbd73e662f22b6",
+                "sha256:857959354ae3a6fa3da6651b966d13b0a8bed6bbc87a0de7b38a549db1d2a359",
+                "sha256:87f37fe5130574ff76c17cab61e7d2538a16f843bb7bca8ebbc4b12de3078596",
+                "sha256:95d5251e4b5ca00061f9d9f3d6fe537247e145a8524ae9fd30a2f8fbce993b5b",
+                "sha256:9d1d3e63a4afdc29bd76ce6aa9d58c771cd1599fbba8cf5057e7860b203710dd",
+                "sha256:a36c5c154f9d42ec176e6e620cb0dd275744aa1d804786a71ac37dc3661a5e95",
+                "sha256:a6a5cb8809091ec9ac03edde9304b3ad82ad4466333432b16d78ef40e0cce0d5",
+                "sha256:ae5e35a2c189d397b91034642cb0eab0e346f776ec2eb44a49a459e6615d6e2e",
+                "sha256:b0f7d4a3df8f06cf49f9f121bead236e328074de6449866515cea4907bbc63d6",
+                "sha256:b75110fb114fa366b29a027d0c9be3709579602ae111ff61674d28c93606acca",
+                "sha256:ba5e697569f84b13640c9e193170e89c13c6244c24400fc57e88724ef610cd31",
+                "sha256:be2a9b390f77fd7676d80bc3cdc4f8edb940d8c198ed2d8c0be1319018c778e1",
+                "sha256:ca1bd81f40adc59011f58159e4aa6445fc585a32bb8ac9badf7a2c1aa23822f2",
+                "sha256:d5d8555d9bfc3f02385c1c37e9f998e2011f0db4f90e250e5bc0c0a85a813085",
+                "sha256:e55e22ac0a30023426564b1059b035973ec82186ddddbac867078435801c7801",
+                "sha256:e90f17980e6ab0f3c2f3730e56d1fe9bcba1891eeea58966e89d352492cc74f4",
+                "sha256:ecbb7b01409e9b782df5ded849c178a0aa7c906cf8c5a67368047daab282b184",
+                "sha256:ed01918d545a38998bfa5902c7c00e0fee90e957ce036a4000a88e3fe2264917",
+                "sha256:edabd457cd23a02965166026fd9bfd196f4324fe6032e866d0f3bd0301cd486f",
+                "sha256:fdf1c1dc5bafc32bc5d08b054f94d659422b05aba244d6be4ddc1c72d9aa70fb"
+            ],
+            "version": "==1.11.5"
+        },
+        "click": {
+            "hashes": [
+                "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13",
+                "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"
+            ],
+            "version": "==7.0"
+        },
+        "flask": {
+            "hashes": [
+                "sha256:2271c0070dbcb5275fad4a82e29f23ab92682dc45f9dfbc22c02ba9b9322ce48",
+                "sha256:a080b744b7e345ccfcbc77954861cb05b3c63786e93f2b3875e0913d44b43f05"
+            ],
+            "index": "pypi",
+            "version": "==1.0.2"
+        },
+        "flask-apscheduler": {
+            "hashes": [
+                "sha256:7911d66e449f412d92a1a6c524217f44f4c40a5c92148c60d5189c6c402f87d0"
+            ],
+            "index": "pypi",
+            "version": "==1.11.0"
+        },
+        "flask-argon2": {
+            "hashes": [
+                "sha256:5c4ec4abfb8802c45f3d475f85f26e8bcb791f4c2f004138bcacb1158b4e3844",
+                "sha256:a40ba10c9ed47269025ffb22bdc8eafc655aac20d6b501ab87a426beda8f2471"
+            ],
+            "index": "pypi",
+            "version": "==0.1.3.0"
+        },
+        "flask-compress": {
+            "hashes": [
+                "sha256:468693f4ddd11ac6a41bca4eb5f94b071b763256d54136f77957cfee635badb3"
+            ],
+            "index": "pypi",
+            "version": "==1.4.0"
+        },
+        "flask-login": {
+            "hashes": [
+                "sha256:c815c1ac7b3e35e2081685e389a665f2c74d7e077cb93cecabaea352da4752ec"
+            ],
+            "index": "pypi",
+            "version": "==0.4.1"
+        },
+        "flask-menu": {
+            "hashes": [
+                "sha256:02adee99456f70fcf4472c5ce04a65a083bce78d39144e4daa38e79a123da7e8",
+                "sha256:d54f604b2d93d1d7bb95c78d9188b5855b54af399f8cd9e64b2feabd138608bf"
+            ],
+            "index": "pypi",
+            "version": "==0.7.0"
+        },
+        "flask-migrate": {
+            "hashes": [
+                "sha256:0c42c34357b24faac63bc6e7af028c8614235f5210eded53802ced450c5392c2",
+                "sha256:8356fa6a02694da34e78da1e38cf91c944b219f4bd4b89493a3b261a305994ab"
+            ],
+            "index": "pypi",
+            "version": "==2.3.1"
+        },
+        "flask-sqlalchemy": {
+            "hashes": [
+                "sha256:3bc0fac969dd8c0ace01b32060f0c729565293302f0c4269beed154b46bec50b",
+                "sha256:5971b9852b5888655f11db634e87725a9031e170f37c0ce7851cf83497f56e53"
+            ],
+            "index": "pypi",
+            "version": "==2.3.2"
+        },
+        "github-webhook": {
+            "hashes": [
+                "sha256:089c7b00c3711565dc14a9b741ef64383d2934a64961676d586b6ab558101030",
+                "sha256:5b13f43f1b764f688426f166c69d0dcdc4a1d88fa87a9017d127ed5752714d12"
+            ],
+            "index": "pypi",
+            "version": "==1.0.2"
+        },
+        "itsdangerous": {
+            "hashes": [
+                "sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19",
+                "sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"
+            ],
+            "version": "==1.1.0"
+        },
+        "jinja2": {
+            "hashes": [
+                "sha256:74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd",
+                "sha256:f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4"
+            ],
+            "version": "==2.10"
+        },
+        "mako": {
+            "hashes": [
+                "sha256:4e02fde57bd4abb5ec400181e4c314f56ac3e49ba4fb8b0d50bba18cb27d25ae"
+            ],
+            "version": "==1.0.7"
+        },
+        "markupsafe": {
+            "hashes": [
+                "sha256:048ef924c1623740e70204aa7143ec592504045ae4429b59c30054cb31e3c432",
+                "sha256:130f844e7f5bdd8e9f3f42e7102ef1d49b2e6fdf0d7526df3f87281a532d8c8b",
+                "sha256:19f637c2ac5ae9da8bfd98cef74d64b7e1bb8a63038a3505cd182c3fac5eb4d9",
+                "sha256:1b8a7a87ad1b92bd887568ce54b23565f3fd7018c4180136e1cf412b405a47af",
+                "sha256:1c25694ca680b6919de53a4bb3bdd0602beafc63ff001fea2f2fc16ec3a11834",
+                "sha256:1f19ef5d3908110e1e891deefb5586aae1b49a7440db952454b4e281b41620cd",
+                "sha256:1fa6058938190ebe8290e5cae6c351e14e7bb44505c4a7624555ce57fbbeba0d",
+                "sha256:31cbb1359e8c25f9f48e156e59e2eaad51cd5242c05ed18a8de6dbe85184e4b7",
+                "sha256:3e835d8841ae7863f64e40e19477f7eb398674da6a47f09871673742531e6f4b",
+                "sha256:4e97332c9ce444b0c2c38dd22ddc61c743eb208d916e4265a2a3b575bdccb1d3",
+                "sha256:525396ee324ee2da82919f2ee9c9e73b012f23e7640131dd1b53a90206a0f09c",
+                "sha256:52b07fbc32032c21ad4ab060fec137b76eb804c4b9a1c7c7dc562549306afad2",
+                "sha256:52ccb45e77a1085ec5461cde794e1aa037df79f473cbc69b974e73940655c8d7",
+                "sha256:5c3fbebd7de20ce93103cb3183b47671f2885307df4a17a0ad56a1dd51273d36",
+                "sha256:5e5851969aea17660e55f6a3be00037a25b96a9b44d2083651812c99d53b14d1",
+                "sha256:5edfa27b2d3eefa2210fb2f5d539fbed81722b49f083b2c6566455eb7422fd7e",
+                "sha256:7d263e5770efddf465a9e31b78362d84d015cc894ca2c131901a4445eaa61ee1",
+                "sha256:83381342bfc22b3c8c06f2dd93a505413888694302de25add756254beee8449c",
+                "sha256:857eebb2c1dc60e4219ec8e98dfa19553dae33608237e107db9c6078b1167856",
+                "sha256:98e439297f78fca3a6169fd330fbe88d78b3bb72f967ad9961bcac0d7fdd1550",
+                "sha256:bf54103892a83c64db58125b3f2a43df6d2cb2d28889f14c78519394feb41492",
+                "sha256:d9ac82be533394d341b41d78aca7ed0e0f4ba5a2231602e2f05aa87f25c51672",
+                "sha256:e982fe07ede9fada6ff6705af70514a52beb1b2c3d25d4e873e82114cf3c5401",
+                "sha256:edce2ea7f3dfc981c4ddc97add8a61381d9642dc3273737e756517cc03e84dd6",
+                "sha256:efdc45ef1afc238db84cb4963aa689c0408912a0239b0721cb172b4016eb31d6",
+                "sha256:f137c02498f8b935892d5c0172560d7ab54bc45039de8805075e19079c639a9c",
+                "sha256:f82e347a72f955b7017a39708a3667f106e6ad4d10b25f237396a7115d8ed5fd",
+                "sha256:fb7c206e01ad85ce57feeaaa0bf784b97fa3cad0d4a5737bc5295785f5c613a1"
+            ],
+            "version": "==1.1.0"
+        },
+        "psycopg2-binary": {
+            "hashes": [
+                "sha256:036bcb198a7cc4ce0fe43344f8c2c9a8155aefa411633f426c8c6ed58a6c0426",
+                "sha256:1d770fcc02cdf628aebac7404d56b28a7e9ebec8cfc0e63260bd54d6edfa16d4",
+                "sha256:1fdc6f369dcf229de6c873522d54336af598b9470ccd5300e2f58ee506f5ca13",
+                "sha256:21f9ddc0ff6e07f7d7b6b484eb9da2c03bc9931dd13e36796b111d631f7135a3",
+                "sha256:247873cda726f7956f745a3e03158b00de79c4abea8776dc2f611d5ba368d72d",
+                "sha256:3aa31c42f29f1da6f4fd41433ad15052d5ff045f2214002e027a321f79d64e2c",
+                "sha256:475f694f87dbc619010b26de7d0fc575a4accf503f2200885cc21f526bffe2ad",
+                "sha256:4b5e332a24bf6e2fda1f51ca2a57ae1083352293a08eeea1fa1112dc7dd542d1",
+                "sha256:570d521660574aca40be7b4d532dfb6f156aad7b16b5ed62d1534f64f1ef72d8",
+                "sha256:59072de7def0690dd13112d2bdb453e20570a97297070f876fbbb7cbc1c26b05",
+                "sha256:5f0b658989e918ef187f8a08db0420528126f2c7da182a7b9f8bf7f85144d4e4",
+                "sha256:649199c84a966917d86cdc2046e03d536763576c0b2a756059ae0b3a9656bc20",
+                "sha256:6645fc9b4705ae8fbf1ef7674f416f89ae1559deec810f6dd15197dfa52893da",
+                "sha256:6872dd54d4e398d781efe8fe2e2d7eafe4450d61b5c4898aced7610109a6df75",
+                "sha256:6ce34fbc251fc0d691c8d131250ba6f42fd2b28ef28558d528ba8c558cb28804",
+                "sha256:73920d167a0a4d1006f5f3b9a3efce6f0e5e883a99599d38206d43f27697df00",
+                "sha256:8a671732b87ae423e34b51139628123bc0306c2cb85c226e71b28d3d57d7e42a",
+                "sha256:8d517e8fda2efebca27c2018e14c90ed7dc3f04d7098b3da2912e62a1a5585fe",
+                "sha256:9475a008eb7279e20d400c76471843c321b46acacc7ee3de0b47233a1e3fa2cf",
+                "sha256:96947b8cd7b3148fb0e6549fcb31258a736595d6f2a599f8cd450e9a80a14781",
+                "sha256:abf229f24daa93f67ac53e2e17c8798a71a01711eb9fcdd029abba8637164338",
+                "sha256:b1ab012f276df584beb74f81acb63905762c25803ece647016613c3d6ad4e432",
+                "sha256:b22b33f6f0071fe57cb4e9158f353c88d41e739a3ec0d76f7b704539e7076427",
+                "sha256:b3b2d53274858e50ad2ffdd6d97ce1d014e1e530f82ec8b307edd5d4c921badf",
+                "sha256:bab26a729befc7b9fab9ded1bba9c51b785188b79f8a2796ba03e7e734269e2e",
+                "sha256:daa1a593629aa49f506eddc9d23dc7f89b35693b90e1fbcd4480182d1203ea90",
+                "sha256:dd111280ce40e89fd17b19c1269fd1b74a30fce9d44a550840e86edb33924eb8",
+                "sha256:e0b86084f1e2e78c451994410de756deba206884d6bed68d5a3d7f39ff5fea1d",
+                "sha256:eb86520753560a7e89639500e2a254bb6f683342af598088cb72c73edcad21e6",
+                "sha256:ff18c5c40a38d41811c23e2480615425c97ea81fd7e9118b8b899c512d97c737"
+            ],
+            "index": "pypi",
+            "version": "==2.7.6.1"
+        },
+        "pycparser": {
+            "hashes": [
+                "sha256:a988718abfad80b6b157acce7bf130a30876d27603738ac39f140993246b25b3"
+            ],
+            "version": "==2.19"
+        },
+        "python-dateutil": {
+            "hashes": [
+                "sha256:063df5763652e21de43de7d9e00ccf239f953a832941e37be541614732cdfc93",
+                "sha256:88f9287c0174266bb0d8cedd395cfba9c58e87e5ad86b2ce58859bc11be3cf02"
+            ],
+            "version": "==2.7.5"
+        },
+        "python-editor": {
+            "hashes": [
+                "sha256:a3c066acee22a1c94f63938341d4fb374e3fdd69366ed6603d7b24bed1efc565"
+            ],
+            "version": "==1.0.3"
+        },
+        "pytz": {
+            "hashes": [
+                "sha256:31cb35c89bd7d333cd32c5f278fca91b523b0834369e757f4c5641ea252236ca",
+                "sha256:8e0f8568c118d3077b46be7d654cc8167fa916092e28320cde048e54bfc9f1e6"
+            ],
+            "version": "==2018.7"
+        },
+        "six": {
+            "hashes": [
+                "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
+                "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
+            ],
+            "version": "==1.12.0"
+        },
+        "sqlalchemy": {
+            "hashes": [
+                "sha256:809547455d012734b4252081db1e6b4fc731de2299f3755708c39863625e1c77"
+            ],
+            "index": "pypi",
+            "version": "==1.2.15"
+        },
+        "tzlocal": {
+            "hashes": [
+                "sha256:4ebeb848845ac898da6519b9b31879cf13b6626f7184c496037b818e238f2c4e"
+            ],
+            "version": "==1.5.1"
+        },
+        "werkzeug": {
+            "hashes": [
+                "sha256:c3fd7a7d41976d9f44db327260e263132466836cef6f91512889ed60ad26557c",
+                "sha256:d5da73735293558eb1651ee2fddc4d0dedcfa06538b8813a2e20011583c9e49b"
+            ],
+            "version": "==0.14.1"
+        }
+    },
+    "develop": {}
+}

+ 53 - 0
app/__init__.py

@@ -0,0 +1,53 @@
+
+"""
+Website for Supremacy-stats
+"""
+
+from flask import Flask
+from flask_sqlalchemy import SQLAlchemy
+from flask_migrate import Migrate
+from flask_login import LoginManager
+from flask_compress import Compress
+from flask_argon2 import Argon2
+
+
+DATABASE_URI = 'postgresql://supindex@localhost/supindex'
+
+
+class Config(object):
+    SCHEDULER_JOBSTORES = {
+        'default': SQLAlchemyJobStore(url=DATABASE_URI)
+    }
+    SCHEDULER_API_ENABLED = True
+    SQLALCHEMY_DATABASE_URI = DATABASE_URI
+    SECRET_KEY = 'g6DGM5y2bVhb0mxdCRELI5m7fnzzoJ2y'
+    SQLALCHEMY_TRACK_MODIFICATIONS = False
+    SEND_FILE_MAX_AGE_DEFAULT = 1296000
+
+
+app = Flask(__name__)
+app.config.from_object(Config())
+app.jinja_env.lstrip_blocks = True
+app.jinja_env.trim_blocks = True
+
+# Compress settings
+COMPRESS_MIMETYPES = [
+    'text/html',
+    'text/css',
+    'text/xml',
+    'application/json',
+    'application/javascript'
+]
+COMPRESS_LEVEL = 6
+COMPRESS_MIN_SIZE = 500
+Compress(app)
+
+db = SQLAlchemy(app)
+migrate = Migrate(app, db)
+argon2 = Argon2(app)
+
+# Login
+login_manager = LoginManager()
+login_manager.init_app(app)
+login_manager.login_view = "login"
+login_manager.login_message_category = "warning"

+ 111 - 0
app/flaskr.py

@@ -0,0 +1,111 @@
+
+"""
+Simple flask thing
+"""
+
+from flask_login import login_required, login_user, logout_user
+from flask_menu import Menu, register_menu
+from flask import render_template, request, redirect, url_for, flash
+from app import app, login_manager, db
+from app.models import User
+
+Menu(app=app)
+
+
+@login_manager.user_loader
+def load_user(user_id):
+    """Return user"""
+    return User.query.get(user_id)
+
+
+@app.route("/login", methods=["GET", "POST"])
+def login():
+    """Handle login page and data"""
+    if request.method == 'POST':
+        email = request.form['email']
+        password = request.form['password']
+        user = User.query.filter(User.email == email).first()
+        if user is not None:
+            if user.check_password(password):
+                login_user(user, remember=True)
+                flash('You were successfully logged in.', 'success')
+                if request.args.get("next") is not None:
+                    return redirect(request.args.get("next"))
+                return redirect(url_for('index'))
+            else:
+                flash('Incorrect password.', 'danger')
+        else:
+            flash('User not found.', 'danger')
+
+        return redirect(url_for('login'))
+    else:
+        return render_template('user/login.html')
+
+
+@app.route("/register", methods=["POST"])
+def register():
+    """Register a new user"""
+    if request.method != "POST":
+        return redirect(url_for('login'))
+
+    if "name" not in request.form or not request.form['name']:
+        flash('Fill in the name.', 'warning')
+        return render_template('user/login.html')
+
+    if "email" not in request.form or not request.form['email']:
+        flash('Fill in the email.', 'warning')
+        return render_template('user/login.html', name=request.form['name'])
+
+    if "password" not in request.form or not request.form['password']:
+        flash('Fill in the password.', 'warning')
+        return render_template(
+            'user/login.html',
+            name=request.form['name'],
+            email=request.form['email']
+        )
+
+    user = User.query.filter(User.name == request.form['name']).first()
+    if user is None:
+        flash('Name not found.', 'warning')
+        return render_template(
+            'user/login.html',
+            name=request.form['name'],
+            email=request.form['email']
+        )
+
+    if user.email is not None:
+        flash('User already taken.', 'warning')
+        return render_template(
+            'user/login.html',
+            name=request.form['name'],
+            email=request.form['email']
+        )
+
+    user.email = request.form['email']
+    user.password = request.form['password']
+
+    db.session.commit()
+    login_user(user, remember=True)
+    flash('Succesfully registered account "%s".' % (user.name), 'success')
+
+    if request.args.get("next") is not None:
+        return redirect(request.args.get("next"))
+    else:
+        return redirect(url_for('index'))
+
+
+@app.route("/logout")
+@login_required
+def logout():
+    """Logout function for users"""
+    logout_user()
+    flash('succesfully logged out.', 'success')
+    return redirect(url_for('login'))
+
+
+@app.route('/')
+@register_menu(app, '.', 'Home')
+def index():
+    """Show homepage"""
+
+    return render_template('site/index.html')

+ 57 - 0
app/models.py

@@ -0,0 +1,57 @@
+
+"""
+All models for module
+"""
+
+from datetime import datetime
+# from sqlalchemy.ext.hybrid import hybrid_method, hybrid_property
+from flask_login import UserMixin
+from app import db, argon2
+
+
+class Base():
+    """Base class for models"""
+    id = db.Column(db.Integer, primary_key=True)
+
+    def __repr__(self):
+        return "<%s(%s)>" % (type(self).__name__, self.id)
+
+
+class User(Base, db.Model, UserMixin):
+    """Model for User"""
+
+    name = db.Column(db.String, unique=True, nullable=False)
+    email = db.Column(db.String(255), unique=True)
+    _password = db.Column("password", db.String(255))
+    registration_at = db.Column(db.DateTime, default=datetime.utcnow)
+
+    @property
+    def password(self):
+        """Return the password"""
+        return self._password
+
+    @password.setter
+    def password(self, password):
+        """Hash password"""
+        self._password = argon2.generate_password_hash(password)
+
+    def check_password(self, password):
+        """Check if password is correct"""
+        return argon2.check_password_hash(self.password, password)
+
+
+class Page(Base, db.Model):
+    """Model for Page"""
+
+    title = db.Column(db.String, nullable=False)
+    datetime = db.Column(db.DateTime, default=datetime.utcnow)
+    source = db.Column(db.String)
+
+    user_id = db.Column(
+        db.Integer,
+        db.ForeignKey("sp_users.id")
+    )
+    user = db.relationship(
+        "User",
+        backref=db.backref("players", lazy="dynamic")
+    )

+ 9 - 0
example.env

@@ -0,0 +1,9 @@
+# Pipenv
+PIPENV_VENV_IN_PROJECT=true
+
+# Flask
+FLASK_APP=app/flaskr.py
+FLASK_DEBUG=1
+FLASK_ENV=development
+FLASK_SKIP_DOTENV=true
+TESTING=True

+ 1 - 0
flask.wsgi

@@ -0,0 +1 @@
+from app.flaskr import app as application

+ 164 - 0
gulpfile.js

@@ -0,0 +1,164 @@
+const { series, parallel, task, src, dest } = require('gulp');
+var gulp = require('gulp');
+var sass = require('gulp-sass');
+var header = require('gulp-header');
+var cleanCSS = require('gulp-clean-css');
+var rename = require('gulp-rename');
+var uglify = require('gulp-uglify');
+var beautify = require('gulp-html-beautify');
+var pkg = require('./package.json');
+var browserSync = require('browser-sync').create();
+var exec = require('child_process').exec;
+
+// Copy third party libraries from /node_modules into /app/static/app/static/vendor
+function vendor_export(cb) {
+	// Bootstrap
+	src([
+		'node_modules/bootstrap/dist/**/*',
+		'!node_modules/bootstrap/dist/css/bootstrap-grid*',
+		'!node_modules/bootstrap/dist/css/bootstrap-reboot*'
+	])
+		.pipe(dest('app/static/vendor/bootstrap'));
+
+	// DataTables
+	src([
+		'node_modules/datatables.net/js/*.js',
+		'node_modules/datatables.net-bs4/js/*.js',
+		'node_modules/datatables.net-bs4/css/*.css',
+		'node_modules/datatables.net-responsive/js/*'
+	])
+		.pipe(dest('app/static/vendor/datatables/'));
+
+	// Font Awesome
+	src([
+		'node_modules/font-awesome/**/*',
+		'!node_modules/font-awesome/{less,less/*}',
+		'!node_modules/font-awesome/{scss,scss/*}',
+		'!node_modules/font-awesome/.*',
+		'!node_modules/font-awesome/*.{txt,json,md}'
+	])
+		.pipe(dest('app/static/vendor/font-awesome'));
+
+	// jQuery
+	src([
+		'node_modules/jquery/dist/*',
+		'!node_modules/jquery/dist/core.js'
+	])
+		.pipe(dest('app/static/vendor/jquery'));
+
+	cb()
+}
+
+function vendor_minify_js(cb) {
+	src([
+		'app/static/vendor/**/*.js',
+		'!app/static/vendor/**/*.min.js'
+	])
+		.pipe(uglify())
+		.pipe(rename({
+			suffix: '.min'
+		}))
+		.pipe(dest('app/static/vendor'))
+
+	cb()
+}
+
+function vendor_minify_css(cb) {
+	src([
+		'app/static/vendor/**/*.css',
+		'!app/static/vendor/**/*.min.css'
+	])
+		.pipe(cleanCSS())
+		.pipe(rename({
+			suffix: '.min'
+		}))
+		.pipe(dest('app/static/vendor'))
+
+	cb()
+}
+
+// Compile SASS
+function css_compile(cb) {
+	src('app/static/sass/**/*.sass')
+		.pipe(sass.sync({
+			outputStyle: 'expanded'
+		}).on('error', sass.logError))
+		.pipe(rename({
+			suffix: '.compiled'
+		}))
+		.pipe(dest('app/static/css'));
+
+	cb()
+}
+
+// Minify CSS
+function css_minify(cb) {
+	src([
+		'app/static/css/**/*.css',
+		'!app/static/css/**/*.min.css'
+	])
+		.pipe(cleanCSS())
+		.pipe(rename({
+			suffix: '.min'
+		}))
+		.pipe(dest('app/static/css'))
+		.pipe(browserSync.stream({match: 'app/static/css/**/*.css'}));
+
+	cb()
+}
+
+// Minify JavaScript
+function js(cb) {
+	src([
+		'app/static/js/**/*.js',
+		'!app/static/js/**/*.min.js'
+	])
+		.pipe(uglify())
+		.pipe(rename({
+			suffix: '.min'
+		}))
+		.pipe(dest('app/static/js'))
+		.pipe(browserSync.stream());
+
+	cb()
+}
+
+// Configure the browserSync task
+function browser_sync() {
+	browserSync.init({
+		notify: false,
+		proxy: '127.0.0.1:5000',
+		open: false
+	});
+}
+
+//Run Flask server
+function run_server() {
+	return exec('flask run');
+}
+
+// Dev task
+function dev() {
+	watch([
+		'app/templates/**/*.html',
+		'app/**/*.py',
+	], browserSync.reload);
+	watch([
+		'app/static/sass/**/*.sass',
+	], ['css:compile', browserSync.reload]);
+	watch([
+		'app/static/css/**/*.css',
+		'!app/static/css/**/*.min.css',
+	], ['css:minify', browserSync.reload]);
+	watch([
+		'app/static/js/**/*.js',
+		'!app/static/js/**/*.min.js'
+	], ['js', browserSync.reload]);
+}
+
+// Tasks
+task('vendor', series(vendor_export, parallel(vendor_minify_js, vendor_minify_css)))
+task("css", series(css_compile, css_minify))
+task('js', js)
+task('dev', series(parallel(run_server, browser_sync), dev))
+task('default', series('vendor', parallel('css', 'js')))

+ 44 - 0
package.json

@@ -0,0 +1,44 @@
+{
+    "title": "SSG",
+    "name": "SSG",
+    "version": "1.0.0",
+    "description": "Static Site Generator",
+    "keywords": [
+        "sass",
+        "html",
+        "responsive",
+        "statics",
+        "app"
+    ],
+    "bugs": {
+        "url": "https://github.com/joostsijm/ssg/issues",
+        "email": "bergjnl@gmail.com"
+    },
+    "license": "MIT",
+    "author": "Joost Sijm",
+    "contributors": [
+        "Joost Sijm (https:/joostsijm.nl/)"
+    ],
+    "repository": {
+        "type": "git",
+        "url": "git@github.com:joostsijm/ssg.git"
+    },
+    "dependencies": {
+        "bootstrap": "4.1.3",
+        "datatables.net-bs4": "1.10.16",
+        "datatables.net-responsive": "^2.2.1",
+        "font-awesome": "4.7.0",
+        "jquery": "3.3.1",
+    },
+    "devDependencies": {
+        "browser-sync": "2.23.6",
+        "gulp": "^4.0.0",
+        "gulp-clean-css": "3.9.2",
+        "gulp-cli": "^2.0.1",
+        "gulp-header": "2.0.1",
+        "gulp-html-beautify": "^1.0.1",
+        "gulp-rename": "^1.2.2",
+        "gulp-sass": "^3.2.0",
+        "gulp-uglify": "3.0.1"
+    }
+}