This web application helps you planning your appointments.
As a provider of appointments (i.e. consultation hours) you can manage times when you are available for different types of appointments (online, in person, different durations) and integrate your Google calendar.
As a client, you can search for available slots and book an appointment. You will receive an invitation from the calendar service of the provider.
Full documentation is available at https://fhswf.github.io/appointme/.
To deploy the application on Kubernetes, you need to create the necessary ConfigMap and Secret resources.
Prepare Configuration:
Detailed configuration templates are provided in backend/k8s/.
backend/k8s/configmap.yaml.example: Use this as a template. Rename it to configmap.yaml (or create a new one). This is the central configuration for both backend and client.
API_URL and BASE_URL here will configure the Backend.REACT_APP_API_URL and REACT_APP_URL here will be injected into the Client.MONGO_URI and CORS_ALLOWED_ORIGINS as needed.backend/k8s/secret.yaml.example: Use this as a template. Rename it to secret.yaml (or create a new one) and set sensitive secrets. Important: Replace the placeholder values (e.g., changeme) with your actual secrets before applying.Apply Resources:
# Example command (after creating the actual files)
kubectl apply -f backend/k8s/configmap.yaml
kubectl apply -f backend/k8s/secret.yaml
Deploy Application:
kubectl apply -f backend/k8s/deployment.yaml
Deploy MCP Server: The MCP server is a separate deployment that integrates with the backend.
kubectl apply -k mcp-server/k8s/base
The Ingress handles routing:
/ -> Client/api -> Backend/mcp -> MCP ServerYou can manage multiple environments (e.g., Staging, Production) using Kustomize overlays located in k8s/overlays/.
Create an Overlay:
Copy an existing overlay (e.g., k8s/overlays/dev) to k8s/overlays/staging or k8s/overlays/prod.
Customize kustomization.yaml:
namespace for the environment.Ingress (to set the correct host) and ConfigMap (see below).Patch ConfigMap:
Create a configmap-patch.yaml in your overlay directory to override environment-specific values like module URLs.
apiVersion: v1
kind: ConfigMap
metadata:
name: appointme
data:
API_URL: "https://staging.example.com/api/v1"
BASE_URL: "https://staging.example.com"
REACT_APP_API_URL: "https://staging.example.com/api/v1"
REACT_APP_URL: "https://staging.example.com"
Deploy:
kubectl apply -k k8s/overlays/staging
To deploy with ArgoCD, you can use the Application manifests provided in k8s/argocd/.
Example k8s/argocd/prod.yaml:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: appointme-prod
namespace: argocd
spec:
project: default
source:
repoURL: 'https://github.com/fhswf/appointme'
targetRevision: HEAD
path: k8s/overlays/prod
destination:
server: https://kubernetes.default.svc
namespace: appointme-prod
syncPolicy:
automated: {}
orphanedResources:
warn: true # Warn about other unknown resources
ignore:
- kind: Secret
name: "argocd-secret"
docker.env and .env| Variable | Description | Required | Default | Source |
|---|---|---|---|---|
MONGO_URI |
Connection string for MongoDB | Yes | ConfigMap: appointme |
|
BASE_URL |
URL of the frontend application (e.g., https://example.com) |
Yes | ConfigMap: appointme |
|
API_URL |
URL of the backend API (e.g., https://api.example.com/api/v1) |
Yes | ConfigMap: appointme |
|
BASE_PATH |
Base path of the application | No | / |
ConfigMap: appointme |
DOMAIN |
Domain for cookie scoping (e.g. example.com) |
No | ConfigMap: appointme (implied) |
|
JWT_SECRET |
Secret key for signing JWTs | Yes | Secret: appointme-secret |
|
CSRF_SECRET |
Secret key for CSRF protection | Yes | Secret: appointme-secret |
|
ADMIN_API_KEY |
API Key for admin/cron operations | Yes | Secret: appointme-secret |
|
SENTRY_DSN |
Sentry DSN for error tracking | No | Secret: appointme-secret |
|
CLIENT_ID |
Google OAuth2 Client ID | No (if Google Login disabled) | Secret: appointme-secret |
|
CLIENT_SECRET |
Google OAuth2 Client Secret | No (if Google Login disabled) | Secret: appointme-secret |
|
DISABLE_GOOGLE_LOGIN |
Set to true to disable Google Login |
No | false |
ConfigMap: appointme |
OIDC_ISSUER |
OIDC Provider URL (e.g., Keycloak Realm URL) | No (if OIDC disabled) | ConfigMap: appointme |
|
OIDC_CLIENT_ID |
OIDC Client ID | No (if OIDC disabled) | ConfigMap: appointme |
|
OIDC_CLIENT_SECRET |
OIDC Client Secret (for Confidential clients) | No | Secret: appointme-secret |
|
EMAIL_FROM |
Email address for sending notifications | Yes | Secret: appointme-secret |
|
EMAIL_PASSWORD |
Password for the email account | Yes | Secret: appointme-secret |
|
ENCRYPTION_KEY |
32-byte hex key for encrypting CalDAV passwords | Yes | Secret: appointme-secret |
|
CONTACT_INFO |
Contact information (Markdown supported) | No | ConfigMap: appointme |
|
REACT_APP_API_URL |
Public API URL for the React Client | Yes | ConfigMap: appointme |
|
REACT_APP_URL |
Public URL of the React Client | Yes | ConfigMap: appointme |
APPointment supports LTI 1.3 (Learning Tools Interoperability) integration through OpenID Connect (OIDC). This allows the application to be integrated into Learning Management Systems (LMS) like Moodle, Canvas, or other LTI-compliant platforms.
The LTI integration is implemented using the standard OIDC authentication flow:
Authentication Flow:
/api/v1/oidc/url/oidc-callback with authorization code/api/v1/oidc/login endpointLTI Role Mapping: The application automatically maps LTI roles from the OIDC claims to internal application roles:
https://purl.imsglobal.org/spec/lti/claim/roles claimstudent roleoidc_controller.tsUser Creation:
sub (subject) claim from the OIDC tokenname claim or derived from email)picture claim, if provided)To enable LTI/OIDC authentication, configure the following environment variables:
| Variable | Description | Example | Source |
|---|---|---|---|
OIDC_ISSUER |
OIDC Provider/LTI Platform URL | https://keycloak.example.com/realms/myrealm |
ConfigMap: appointme |
OIDC_CLIENT_ID |
OIDC Client ID registered with the provider | appointme |
ConfigMap: appointme |
OIDC_CLIENT_SECRET |
Client Secret (for confidential clients) | your-secret-here |
Secret: appointme-secret |
OIDC_NAME |
Display name for the login button (optional) | Campus-ID |
ConfigMap: appointme |
OIDC_ICON |
Icon path for the login button (optional) | /fh-swf.svg |
ConfigMap: appointme |
LTI_ISSUER |
LTI Issuer URL (Overrides OIDC_ISSUER for LTI) | https://moodle.example.com |
ConfigMap: appointme |
LTI_CLIENT_ID |
LTI Client ID (Overrides OIDC_CLIENT_ID for LTI) | client-123 |
ConfigMap: appointme |
LTI_CLIENT_SECRET |
LTI Client Secret (Overrides OIDC_CLIENT_SECRET) | secret-456 |
Secret (Implicit?) |
LTI_AUTH_ENDPOINT |
LTI Authorization Endpoint | https://moodle.example.com/mod/lti/auth.php |
ConfigMap: appointme |
LTI_TOKEN_ENDPOINT |
LTI Token Endpoint | https://moodle.example.com/mod/lti/token.php |
ConfigMap: appointme |
LTI_JWKS_URI |
LTI JWKS URI | https://moodle.example.com/mod/lti/certs.php |
ConfigMap: appointme |
Create a new client in Keycloak:
appointme)openid-connectconfidential (if using client secret) or publichttps://your-domain.com/oidc-callbackConfigure LTI Claims (if using LTI):
https://purl.imsglobal.org/spec/lti/claim/roles in the ID tokenSet environment variables in your deployment with the Keycloak realm URL and client credentials
Most modern LMS platforms support LTI 1.3 with OIDC. When configuring AppointMe as an External Tool (LTI 1.3), use the following settings:
Moodle Tool Configuration:
[BASE_URL] (e.g., https://appointme.example.com)client_secret_basic authentication (Client ID + Client Secret) to communicate with the LMS. It DOES NOT sign requests with a private key (which is what private_key_jwt uses).
* However, some LMS versions (like Moodle) may structurally require a Public Key to be present in the configuration form.
* If required, you can generate a "dummy" key pair to satisfy the form:openssl genrsa -out private.key 2048
openssl rsa -in private.key -pubout -out public.key
* *Paste `public.key` into Moodle. AppointMe does NOT need the private key and will NOT use this key pair. It validates the JWTs sent BY Moodle using Moodle's own public keys (fetched automatically via the Issuer URL).*
[API_URL]/api/v1/oidc/init (e.g., https://api.appointme.example.com/api/v1/oidc/init)[BASE_URL]/oidc-callback (e.g., https://appointme.example.com/oidc-callback)https://purl.imsglobal.org/spec/lti/claim/roles.Environment Configuration (in AppointMe):
OIDC_ISSUER to the Platform ID / Issuer URL provided by Moodle (e.g., https://moodle.example.com).OIDC_CLIENT_ID to the Client ID generated by Moodle.OIDC_CLIENT_SECRET to the Client Secret provided by Moodle (or generate one if using specific plugins).CORS_ALLOWED_ORIGINSGET /api/v1/oidc/config - Check if OIDC is enabledGET /api/v1/oidc/url - Get authorization URL for authenticationPOST /api/v1/oidc/login - Complete authentication with authorization codeThe LTI/OIDC integration is implemented in:
backend/src/controller/oidc_controller.tsbackend/src/routes/oidc_routes.tsclient/src/pages/OidcCallback.tsxFor detailed implementation, see the source code in the repository.