diff --git a/README.md b/README.md index 8a4f4a5..7a86370 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,43 @@ # Jumpstart -## Author: Beckett Jenen -[![standard-readme compliant](https://img.shields.io/badge/readme%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme) +A graphical interface that displays important information at the entrance of CSH. -A graphical interface that runs on a RPI in the lobby of my dorm. +See it live [here](https://jumpstart.csh.rit.edu)! ## Install This project uses [Python](http://nodejs.org), [Flask](https://npmjs.com), SQL, HTML/CSS, and Javascript. -1. Download the zip file and open the code. -2. Navigate into the folder. -3. Run `pip install -r requirements.txt` -4. Run `flask run` -5. DM @drno in CSH slack, or email for secrets package +1. Clone and cd into the repo: `git clone https://github.com/ComputerScienceHouse/Jumpstart` +2. Run `pip install -r requirements.txt` +3. Ask opcomm for secrets - The secrets package follows the directory structure: src/ secrets/ client_secrets.json -6. Results +4. Run `flask --app jumpstart run` +5. Results -## Usage +Jumpstart expects the following environment variables to be defined: +``` +FLASK_APP=jumpstart:App +JUMPSTART_API_KEYS=KEYS +TZ=TIMEZONE +SENTRY_DSN=LINK +``` +## Docker -Go [here](https://jumpstart.csh.rit.edu) +1. Ensure you are in the project root, then build the image locally with `docker built -t jumpstart .` +2. Run with the following: (Be sure to update env variables) +``` +docker run \ + -e FLASK_RUN_HOST=0.0.0.0 \ + -e FLASK_APP=jumpstart:App \ + -e JUMPSTART_API_KEYS=KEYS \ + -e TZ=America/New_York \ + -e SENTRY_DSN=LINK \ + -e GUNICORN_CMD_ARGS="-b=0.0.0.0:5000" \ + -v ./secrets:/usr/local/jumpstart/secrets \ + -p 5000:5000 \ + jumpstart +``` \ No newline at end of file diff --git a/dockerfile b/dockerfile new file mode 100644 index 0000000..5174dad --- /dev/null +++ b/dockerfile @@ -0,0 +1,17 @@ +FROM python:3.13 +WORKDIR /usr/local/jumpstart + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY jumpstart jumpstart + +EXPOSE 5000 + +RUN useradd jumpstart +RUN chown jumpstart /usr/local/jumpstart +RUN mkdir -p /usr/local/var +RUN chown jumpstart:jumpstart /usr/local/var +USER jumpstart + +CMD ["gunicorn", "jumpstart:app"] \ No newline at end of file diff --git a/jumpstart/__init__.py b/jumpstart/__init__.py index 64ce147..ae016a1 100644 --- a/jumpstart/__init__.py +++ b/jumpstart/__init__.py @@ -39,34 +39,41 @@ integrations = [FlaskIntegration()] ) -App = Flask(__name__) +app = Flask(__name__) auth = HTTPTokenAuth(scheme='Token') api_keys = os.environ.get('JUMPSTART_API_KEYS') tokens = api_keys.split(',') if api_keys else [] -App.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True -App.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db' -db = SQLAlchemy(App) + +app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True +app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db' + +db = SQLAlchemy(app) from jumpstart.models import File, Ann if not os.path.exists(os.path.join(os.getcwd(), "site.db")): - db.create_all() - -# Initializes the database for Files -file = File(title="Jumpstart.exe") -db.session.query(File).delete() -db.session.commit() -db.session.add(file) -db.session.commit() - -# Initializes the database for Announcements -ann = Ann(title="Have a great day!") -db.session.query(Ann).delete() -db.session.commit() -db.session.add(ann) -db.session.commit() + with app.app_context(): + db.create_all() + +# Prepare database +# geese: If the database is cleared every boot, +# why can't it just be a damn variable? +with app.app_context(): + # Initializes the database for Files + file = File(title="Jumpstart.exe") + db.session.query(File).delete() + db.session.commit() + db.session.add(file) + db.session.commit() + + # Initializes the database for Announcements + ann = Ann(title="Have a great day!") + db.session.query(Ann).delete() + db.session.commit() + db.session.add(ann) + db.session.commit() @auth.verify_token def verify_token(token): @@ -77,8 +84,8 @@ def verify_token(token): limiter = Limiter( - App, - key_func=get_remote_address, + get_remote_address, + app=app, default_limits=["13 per minute", "1 per second"], ) @@ -86,11 +93,12 @@ def verify_token(token): def ip_whitelist(): return request.remote_addr == "127.0.0.1" -@App.route('/') + +@app.route('/') def index(): return render_template('index.html') -@App.route('/calendar', methods=['GET']) +@app.route('/calendar', methods=['GET']) @limiter.limit("3/minute") @limiter.limit("1/second") def calendar(): @@ -136,7 +144,7 @@ def calendar(): event_list = {'data': final_events} return jsonify(event_list) -@App.route('/get-announcement', methods=['GET']) +@app.route('/get-announcement', methods=['GET']) @limiter.limit("13/minute") @limiter.limit("1/second") def get_announcement(): @@ -144,7 +152,7 @@ def get_announcement(): announcement_post = {'data' : str(ann)} return jsonify(announcement_post) -@App.route("/update-announcement", methods=["POST"]) +@app.route("/update-announcement", methods=["POST"]) @auth.login_required @limiter.limit("3/hour") @limiter.limit("2/minute") @@ -157,7 +165,7 @@ def update_announcement(): db.session.commit() return "Announcement Updated" -@App.route('/get-harold', methods=['GET']) +@app.route('/get-harold', methods=['GET']) @limiter.limit("13/minute") @limiter.limit("1/second") def get_harold(): @@ -165,7 +173,7 @@ def get_harold(): filename = {'data': str(file)} return jsonify(filename) -@App.route("/update-harold", methods=["POST"]) +@app.route("/update-harold", methods=["POST"]) @auth.login_required def update_harold(): req_data = request.get_json() @@ -178,7 +186,7 @@ def update_harold(): if __name__ == '__main__': App.run(debug=True) -@App.route('/showerthoughts', methods=['GET']) +@app.route('/showerthoughts', methods=['GET']) @limiter.limit("3/minute") @limiter.limit("1/second") def showerthoughts(): diff --git a/requirements.txt b/requirements.txt index 9628066..b66216e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,14 +1,15 @@ -Babel~=2.6.0 -Flask~=1.1.1 -flask_httpauth~=3.3.0 -Flask_limiter~=1.1.0 -Flask-SQLAlchemy~=2.3.2 -google-api-python-client>=1.7.11 -google-auth-httplib2~=0.0.3 -google-auth-oauthlib~=0.4.1 -gunicorn~=19.9.0 +Babel~=2.17.0 +Flask~=3.1.2 +flask_httpauth~=4.8.0 +Flask_limiter~=3.13 +Flask-SQLAlchemy~=3.1.1 +google-api-python-client>=2.181 +google-auth-httplib2~=0.2.0 +google-auth-oauthlib~=1.2.2 +gunicorn~=23.0.0 oauth2client~=4.1.3 -profanityfilter~=2.0.4 -python-dateutil~=2.6.1 -sentry-sdk[flask]~=0.13.2 -SQLAlchemy~=1.3.7 \ No newline at end of file +profanityfilter~=2.1.0 +python-dateutil~=2.9.0.post0 +sentry-sdk[flask]~=2.38.0 +SQLAlchemy~=2.0.43 +pytz~=2025.2 \ No newline at end of file