From 829f507b76eb426873aa073d209571f1b663bdfa Mon Sep 17 00:00:00 2001 From: "Noah Hanford (spaced)" Date: Sun, 12 Apr 2026 14:38:44 -0400 Subject: [PATCH 1/9] add rum config and compose file --- .env.example | 13 +++++++++++++ config.env.py | 2 +- docker-compose.yml | 16 +++++++++++++++ profiles/__init__.py | 17 ++++++++-------- profiles/templates/include/head.html | 29 ++++++++++++++++++++++++++++ 5 files changed, 68 insertions(+), 9 deletions(-) create mode 100644 .env.example create mode 100644 docker-compose.yml diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..3bec992 --- /dev/null +++ b/.env.example @@ -0,0 +1,13 @@ +PROFILES_SERVER_NAME=localhost:8080 + +DEBUG=true + +PROFILES_OIDC_CLIENT_ID=profiles-dev +PROFILES_OIDC_CLIENT_SECRET= +PROFILES_OIDC_LOGOUT_REDIRECT_URI=http://localhost:8080/logout + +LDAP_BIND_DN=uid=yourusername,cn=users,cn=accounts,dc=csh,dc=rit,dc=edu +LDAP_BIND_PASS= + +DATADOG_ENV=local +DATADOG_APP_VERSION=unset diff --git a/config.env.py b/config.env.py index 6cbe927..df6ceb6 100644 --- a/config.env.py +++ b/config.env.py @@ -7,7 +7,7 @@ SENTRY_DSN = env.get("PROFILES_SENTRY_DSN", "") # Flask config -DEBUG = False +DEBUG = os.environ.get("DEBUG", "false").lower() == "true" IP = os.environ.get('PROFILES_IP', 'localhost') PORT = os.environ.get('PROFILES_PORT', 8080) SERVER_NAME = os.environ.get('PROFILES_SERVER_NAME', 'profiles.csh.rit.edu') diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..dad48d5 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,16 @@ +version: "3" +services: + profiles: + build: . + container_name: profiles + ports: + - "127.0.0.1:8080:8080" + env_file: + - .env + develop: + watch: + - action: sync+restart + path: ./profiles + target: /opt/profiles/profiles + ignore: + - __pycache__ diff --git a/profiles/__init__.py b/profiles/__init__.py index 2289e29..ccc12ac 100644 --- a/profiles/__init__.py +++ b/profiles/__init__.py @@ -90,12 +90,12 @@ def home(info=None): @before_request def user(uid=None, info=None): try: - return render_template("profile.html", info=info, member_info=get_member_info(uid)) + return render_template("profile.html", info=info, member_info=get_member_info(uid), **app.config['DATADOG_RUM_CONFIG']) except BadQueryError as bqe: # ldap_get_member() returns a BadQueryError if getting the user's information fails. # Flask already treats a stray BadQueryError as a 404, but actually handling it prevents the traceback # from getting dumped into the log. - return render_template("404.html", message=bqe), 404 + return render_template("404.html", message=bqe, *app.config['DATADOG_RUM_CONFIG']), 404 @app.route("/results", methods=["POST"]) @@ -116,7 +116,7 @@ def search(searched=None, info=None): if len(members) == 1: return redirect("/user/" + members[0].uid, 302) return render_template( - "listing.html", info=info, title="Search Results: " + searched, members=members + "listing.html", info=info, title="Search Results: " + searched, members=members, *app.config['DATADOG_RUM_CONFIG'] ) @@ -128,7 +128,7 @@ def group(_group=None, info=None): if _group == "eboard": return render_template( - "listing.html", info=info, title=group_desc, members=ldap_get_eboard() + "listing.html", info=info, title=group_desc, members=ldap_get_eboard(), *app.config['DATADOG_RUM_CONFIG'] ) return render_template( @@ -136,6 +136,7 @@ def group(_group=None, info=None): info=info, title=group_desc, members=_ldap_get_group_members(_group), + *app.config['DATADOG_RUM_CONFIG'] ) @@ -144,7 +145,7 @@ def group(_group=None, info=None): @before_request def year(_year=None, info=None): return render_template( - "listing.html", info=info, title="Year: " + _year, members=ldap_get_year(_year) + "listing.html", info=info, title="Year: " + _year, members=ldap_get_year(_year), *app.config['DATADOG_RUM_CONFIG'] ) @@ -181,7 +182,7 @@ def image(uid): try: return get_image(uid) except BadQueryError as bqe: - return render_template("404.html", message=bqe), 404 + return render_template("404.html", message=bqe, *app.config['DATADOG_RUM_CONFIG']), 404 @app.route("/clearcache") @@ -214,9 +215,9 @@ def clear_cache(info=None): @app.errorhandler(500) def handle_internal_error(e): if isinstance(e, NotFound): - return render_template("404.html", message=str(e)), 404 + return render_template("404.html", message=str(e), *app.config['DATADOG_RUM_CONFIG']), 404 if isinstance(e.original_exception, BadQueryError): - return render_template("404.html", message=e.original_exception), 404 + return render_template("404.html", message=e.original_exception, *app.config['DATADOG_RUM_CONFIG']), 404 raise e.original_exception diff --git a/profiles/templates/include/head.html b/profiles/templates/include/head.html index 521ba71..54a93b7 100644 --- a/profiles/templates/include/head.html +++ b/profiles/templates/include/head.html @@ -28,4 +28,33 @@ + + From 4ae213e7cf763318a63fc574616d1f16a224cbb1 Mon Sep 17 00:00:00 2001 From: Noah Hanford Date: Sun, 12 Apr 2026 14:54:42 -0400 Subject: [PATCH 2/9] Update README.md to document compose --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4581f59..8334448 100755 --- a/README.md +++ b/README.md @@ -67,11 +67,16 @@ Reach out to an RTP to get OIDC credentials that will allow you to develop local ```uid={CSH User Name},cn=users,cn=accounts,dc=csh,dc=rit,dc=edu``` - ```LDAP_BIND_PASS``` is your CSH password. If you did everything right, you should be able to run ```python app.py``` and develop locally. +Running with Docker +-------------------- + +Alternatively, you can run profiles using docker or podman compose. You can configure the environment in same way as above, using a `config.py` file, or you can copy the `.env.example` file to `.env` and configure it there. + +The docker-compose file is also set up for automatic redeploying with watch if you run with `podman compose up --watch --build` Code Standards ------------ From 8ab2ee3d2513d34b12e714dd6b8187c5179c8dde Mon Sep 17 00:00:00 2001 From: "Noah Hanford (spaced)" Date: Sun, 12 Apr 2026 15:03:54 -0400 Subject: [PATCH 3/9] Updated some stuff, added env vars back --- config.env.py | 5 +++++ docker-compose.yml | 3 ++- profiles/templates/include/head.html | 12 +++++------- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/config.env.py b/config.env.py index df6ceb6..d8ddfb1 100644 --- a/config.env.py +++ b/config.env.py @@ -25,3 +25,8 @@ LDAP_BIND_DN = env.get("LDAP_BIND_DN", default="cn=profiles,ou=Apps,dc=csh,dc=rit,dc=edu") LDAP_BIND_PASS = env.get("LDAP_BIND_PW", default=None) + +DATADOG_RUM_CONFIG = { + 'DATADOG_ENV': os.environ.get('DATADOG_ENV', 'local'), + 'DATADOG_APP_VERSION': os.environ.get('DATADOG_APP_VERSION', 'unset'), +} diff --git a/docker-compose.yml b/docker-compose.yml index dad48d5..33a0a31 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,7 +6,8 @@ services: ports: - "127.0.0.1:8080:8080" env_file: - - .env + - path: .env + required: false develop: watch: - action: sync+restart diff --git a/profiles/templates/include/head.html b/profiles/templates/include/head.html index 54a93b7..4de3859 100644 --- a/profiles/templates/include/head.html +++ b/profiles/templates/include/head.html @@ -41,18 +41,16 @@ clientToken: 'pub2f0696fe063adeb0dc204547f4584f55', site: 'datadoghq.com', service: 'profiles', - env: '{{ DATADOG_ENV }}', // e.g. 'prod', 'staging-1', 'dev' - version: '{{ DATADOG_APP_VERSION }}', // e.g. '1.0.0' + env: '{{ DATADOG_ENV }}', + version: '{{ DATADOG_APP_VERSION }}', sessionSampleRate: 100, // capture 100% of sessions - sessionReplaySampleRate: 100, // capture 20% of sessions with replay + sessionReplaySampleRate: 100, // capture 100% of sessions with replay trackResources: true, // Enable Resource tracking trackUserInteractions: true, // Enable Action tracking trackLongTasks: true, // Enable Long Tasks tracking - // ----- Recommended options (commented by default) ----- - - allowedTracingUrls: [ 'http://localhost:8080', 'https://profiles.csh.rit.edu', 'https://profiles-dev.csh.rit.edu'], // Enable distributed tracing - defaultPrivacyLevel: 'mask-user-input', // or 'allow' / 'mask-all' + allowedTracingUrls: [ 'http://localhost:8080', 'https://profiles.csh.rit.edu', 'https://profiles-dev.csh.rit.edu'], + defaultPrivacyLevel: 'mask-user-input', }); }); From 43e34089f687cada08f74d6c3f5733a67dc5b488 Mon Sep 17 00:00:00 2001 From: Noah Hanford Date: Sun, 12 Apr 2026 15:04:54 -0400 Subject: [PATCH 4/9] Update README.md --- README.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8334448..649183b 100755 --- a/README.md +++ b/README.md @@ -61,13 +61,23 @@ to SERVER_NAME = os.environ.get('PROFILES_SERVER_NAME', 'localhost:8080') ``` -Reach out to an RTP to get OIDC credentials that will allow you to develop locally behind OIDC auth. +#### OIDC + +Reach out to an RTP to get OIDC credentials that will allow you to develop locally behind OIDC auth. You will need `PROFILES_OIDC_CLIENT_ID` and `PROFILES_OIDC_CLIENT_SECRET` + +#### LDAP ```LDAP_BIND_DN``` is your CSH DN. It is in the following format: ```uid={CSH User Name},cn=users,cn=accounts,dc=csh,dc=rit,dc=edu``` - ```LDAP_BIND_PASS``` is your CSH password. +```LDAP_BIND_PASS``` is your CSH password. + +#### DATADOG + +```DATADOG_ENV``` is the env where you are developing, should stay local for local development + +```DATADOG_APP_VERSION``` is the version of your app, no real values yet since versions are iffy, could be commit hash in the future If you did everything right, you should be able to run ```python app.py``` and develop locally. From 3db912d058e550e05e19633ec84ba657c9faf465 Mon Sep 17 00:00:00 2001 From: Noah Hanford Date: Sun, 12 Apr 2026 15:06:12 -0400 Subject: [PATCH 5/9] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 649183b..eb2e054 100755 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ Reach out to an RTP to get OIDC credentials that will allow you to develop local ```DATADOG_ENV``` is the env where you are developing, should stay local for local development -```DATADOG_APP_VERSION``` is the version of your app, no real values yet since versions are iffy, could be commit hash in the future +```DATADOG_APP_VERSION``` is the version of your app, set to commit hash on okd If you did everything right, you should be able to run ```python app.py``` and develop locally. From c041ac9dc76fdf3c3409deafe2eb472213244cda Mon Sep 17 00:00:00 2001 From: "Noah Hanford (spaced)" Date: Sun, 12 Apr 2026 15:16:25 -0400 Subject: [PATCH 6/9] make git hash be version in rum --- .env.example | 1 - config.env.py | 5 ++++- profiles/utils.py | 6 ++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.env.example b/.env.example index 3bec992..2a73383 100644 --- a/.env.example +++ b/.env.example @@ -10,4 +10,3 @@ LDAP_BIND_DN=uid=yourusername,cn=users,cn=accounts,dc=csh,dc=rit,dc=edu LDAP_BIND_PASS= DATADOG_ENV=local -DATADOG_APP_VERSION=unset diff --git a/config.env.py b/config.env.py index d8ddfb1..4505441 100644 --- a/config.env.py +++ b/config.env.py @@ -2,6 +2,7 @@ import random import string from os import environ as env +import subprocess # Sentry DSN SENTRY_DSN = env.get("PROFILES_SENTRY_DSN", "") @@ -26,7 +27,9 @@ LDAP_BIND_DN = env.get("LDAP_BIND_DN", default="cn=profiles,ou=Apps,dc=csh,dc=rit,dc=edu") LDAP_BIND_PASS = env.get("LDAP_BIND_PW", default=None) +GIT_HASH = subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD']).decode('utf-8').rstrip() + DATADOG_RUM_CONFIG = { 'DATADOG_ENV': os.environ.get('DATADOG_ENV', 'local'), - 'DATADOG_APP_VERSION': os.environ.get('DATADOG_APP_VERSION', 'unset'), + 'DATADOG_APP_VERSION': os.environ.get('DATADOG_APP_VERSION', GIT_HASH), } diff --git a/profiles/utils.py b/profiles/utils.py index 505778f..d026219 100644 --- a/profiles/utils.py +++ b/profiles/utils.py @@ -1,6 +1,5 @@ # Credit to Liam Middlebrook and Ram Zallan # https://github.com/liam-middlebrook/gallery -import subprocess import base64 import datetime @@ -12,6 +11,7 @@ import ldap from profiles import _ldap +import profiles from profiles.ldap import (ldap_get_calendar, ldap_get_member, ldap_get_pronouns, @@ -25,13 +25,11 @@ def before_request(func): @wraps(func) def wrapped_function(*args, **kwargs): - git_revision = subprocess.check_output( - ['git', 'rev-parse', '--short', 'HEAD']).decode('utf-8').rstrip() uuid = str(session["userinfo"].get("sub", "")) uid = str(session["userinfo"].get("preferred_username", "")) user_obj = _ldap.get_member(uid, uid=True) info = { - "git_revision": git_revision, + "git_revision": profiles.app.config['GIT_HASH'], "uuid": uuid, "uid": uid, "user_obj": user_obj, From 8d09e5a1a92da0a89ba2e9db2c162264691a659b Mon Sep 17 00:00:00 2001 From: Noah Hanford Date: Sun, 12 Apr 2026 15:17:13 -0400 Subject: [PATCH 7/9] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index eb2e054..f6d12da 100755 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ Reach out to an RTP to get OIDC credentials that will allow you to develop local ```DATADOG_ENV``` is the env where you are developing, should stay local for local development -```DATADOG_APP_VERSION``` is the version of your app, set to commit hash on okd +```DATADOG_APP_VERSION``` is the version of your app, when unset defaults to git commit hash If you did everything right, you should be able to run ```python app.py``` and develop locally. From 3f0d23cb3b805c90a61a964a05d7bbf4332100c2 Mon Sep 17 00:00:00 2001 From: "Noah Hanford (spaced)" Date: Sun, 12 Apr 2026 15:19:51 -0400 Subject: [PATCH 8/9] Fixing linting --- profiles/__init__.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/profiles/__init__.py b/profiles/__init__.py index ccc12ac..c04de57 100644 --- a/profiles/__init__.py +++ b/profiles/__init__.py @@ -90,7 +90,8 @@ def home(info=None): @before_request def user(uid=None, info=None): try: - return render_template("profile.html", info=info, member_info=get_member_info(uid), **app.config['DATADOG_RUM_CONFIG']) + return render_template("profile.html", info=info, + member_info=get_member_info(uid), **app.config['DATADOG_RUM_CONFIG']) except BadQueryError as bqe: # ldap_get_member() returns a BadQueryError if getting the user's information fails. # Flask already treats a stray BadQueryError as a 404, but actually handling it prevents the traceback @@ -116,7 +117,8 @@ def search(searched=None, info=None): if len(members) == 1: return redirect("/user/" + members[0].uid, 302) return render_template( - "listing.html", info=info, title="Search Results: " + searched, members=members, *app.config['DATADOG_RUM_CONFIG'] + "listing.html", info=info, title="Search Results: " + searched, + members=members, *app.config['DATADOG_RUM_CONFIG'] ) @@ -128,7 +130,8 @@ def group(_group=None, info=None): if _group == "eboard": return render_template( - "listing.html", info=info, title=group_desc, members=ldap_get_eboard(), *app.config['DATADOG_RUM_CONFIG'] + "listing.html", info=info, title=group_desc, + members=ldap_get_eboard(), *app.config['DATADOG_RUM_CONFIG'] ) return render_template( From 1dbea022f2f3338408dce6959bd9481b682e8ab4 Mon Sep 17 00:00:00 2001 From: "Noah Hanford (spaced)" Date: Sun, 12 Apr 2026 15:46:27 -0400 Subject: [PATCH 9/9] more fixing lint --- profiles/__init__.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/profiles/__init__.py b/profiles/__init__.py index c04de57..4ae4181 100644 --- a/profiles/__init__.py +++ b/profiles/__init__.py @@ -90,8 +90,10 @@ def home(info=None): @before_request def user(uid=None, info=None): try: - return render_template("profile.html", info=info, - member_info=get_member_info(uid), **app.config['DATADOG_RUM_CONFIG']) + return render_template( + "profile.html", info=info, + member_info=get_member_info(uid), **app.config['DATADOG_RUM_CONFIG'] + ) except BadQueryError as bqe: # ldap_get_member() returns a BadQueryError if getting the user's information fails. # Flask already treats a stray BadQueryError as a 404, but actually handling it prevents the traceback @@ -148,7 +150,8 @@ def group(_group=None, info=None): @before_request def year(_year=None, info=None): return render_template( - "listing.html", info=info, title="Year: " + _year, members=ldap_get_year(_year), *app.config['DATADOG_RUM_CONFIG'] + "listing.html", info=info, title="Year: " + _year, + members=ldap_get_year(_year), *app.config['DATADOG_RUM_CONFIG'] )