Vacal is a modern absence tracking system for managing calendar-based scheduling processes. Despite the name, it supports tracking all kinds of absences, not just vacations.
- Manage teams and team members effectively.
- Organize and track vacations and other absences.
- Keep a history of all day modifications. Timestamps are shown in your local timezone.
- Visualize absences (vacations, sick leave, etc.), weekends, and public holidays in an intuitive interface.
- Ideal for both local and distributed teams, enhancing team coordination and planning.
./run_docker_compose_local.sh- Access on http://localhost:5173
- Copy the backend environment template:
cp backend/.env.docker-compose.template backend/.env - Start the full stack with live reload:
./run_dev.sh - The API runs on http://localhost:8000 and the React app on http://localhost:5173 with automatic rebuilds.
- Stop the stack with
Ctrl+Cwhen you are done.
The script expects the
docker-composeCLI to be available (Docker Desktop keeps a V1-compatible shim even with Compose V2).
- Deploy or use existing MongoDB server with enabled authentication.
- Use prebuilt Docker container from this repository packages.
- Provide to container relevant environment variables defined in
backend/.env.template.
- Create
.env.production.localfromfrontend/.env.exampleand setVITE_API_URL. - Build with
npm run build. - Use built static sources from the
distfolder.
- For USERNAME/PASSWORD AUTHENTICATION generate a string like this run:
openssl rand -hex 32and setAUTHENTICATION_SECRET_KEYin the environment.
Vacal implements a secure token refresh system with short-lived access tokens and revocable refresh tokens:
- Access tokens expire after 15 minutes (configurable via
ACCESS_TOKEN_EXPIRE_MINUTES) - Refresh tokens expire after 7 days (configurable via
REFRESH_TOKEN_EXPIRE_DAYS) - The frontend automatically refreshes tokens before expiration for seamless user experience
- Refresh tokens are stored securely in the database with hashed values and can be revoked on logout
- Token rotation on each refresh prevents replay attacks
- On 401 responses, the frontend attempts automatic token refresh before logging the user out
- Deployments that switch refresh token format may invalidate existing sessions and require one re-login
This approach significantly reduces the security risk compared to long-lived tokens:
- Stolen access tokens are only valid for 15 minutes
- Refresh tokens can be revoked server-side (e.g., on logout or security breach)
- Token rotation helps detect token theft
- See https://core.telegram.org/widgets/login
- Configure TELEGRAM_BOT_TOKEN and TELEGRAM_BOT_USERNAME in the backend .env
- Tip for local testing: https://stackoverflow.com/questions/61964889/testing-telegram-login-widget-locally
- Create an OAuth 2.0 Client ID in the Google Cloud Console and set
GOOGLE_CLIENT_IDin the backend environment. - The frontend should obtain an ID token from Google and send it to the
/google-loginendpoint. - On first login the backend links the Google account to an existing user; later logins use the stored Google ID.
- Logged-in users can link their Google account from the user management settings via the
/google-connectendpoint.
- The backend uses TOTP-based MFA via
pyotp. - Every user has an
mfa_secretgenerated automatically and the/tokenendpoint enforces MFA. - On the first login, the server returns a QR code provisioning URI so you can scan it with your authenticator app and confirm the OTP.
- The login page first asks for your username and password. If MFA isn't configured yet, you'll see a QR code to scan and an OTP field. Returning users are prompted for their one-time code after submitting credentials.
Teams expose a read-only iCalendar feed. Subscribe using
/calendar/{team_id}?user_api_key={user_api_key} in external calendars like
Google Calendar. The user API key is generated for each user and allows access
to be revoked when the user is removed. The feed returns
all stored absences, so no dates need to be provided in the subscription URL.
