feat: move config.py to config.example.py, exclude config.py from git tracking

- Copy config.py to config.example.py as template (tracked in git)
- Add config.py to .gitignore (local config per developer)
- Update README.md with configuration setup instructions
- Fix outdated references (TinyDB -> SQLite, add missing files)
- Persist goal selection across task and note pages
This commit is contained in:
Yuyao Huang 2026-05-09 10:12:37 +08:00
parent 3c325bdb0f
commit 2629e8f2cd
4 changed files with 41 additions and 30 deletions

3
.gitignore vendored
View File

@ -23,6 +23,9 @@ uv.lock
.DS_Store
Thumbs.db
# Configuration (each developer must create their own)
config.py
# Environment variables
.env
.env.local

View File

@ -26,6 +26,9 @@ A web-based task management application focused on goal-oriented task tracking w
git clone <repository-url>
cd GoalsBreakDown
# Create your local configuration from the example
cp config.example.py config.py
# Install dependencies with uv
uv sync
@ -44,32 +47,33 @@ The application will start at **http://127.0.0.1:5000**
## Configuration
Edit `config.py` to customize settings:
Local configuration is managed via `config.py` (not tracked in git). Use `config.example.py` as a template:
```python
# Database path
DB_PATH = "data/db.json"
# Default admin credentials (change these!)
DEFAULT_ADMIN_USERNAME = "admin"
DEFAULT_ADMIN_PASSWORD = "admin123"
# Default max goals for new users
DEFAULT_MAX_GOALS = 5
# Flask settings
SECRET_KEY = "your-secret-key-here" # Change in production
DEBUG = True
HOST = "0.0.0.0"
PORT = 5000
```bash
# Copy the example config and edit it
cp config.example.py config.py
```
Available settings in `config.py`:
| Setting | Description | Default |
|---|---|---|
| `DB_PATH` | SQLite database path | `data/db.sqlite` |
| `DEFAULT_ADMIN_USERNAME` | Default admin username | `admin` |
| `DEFAULT_ADMIN_PASSWORD` | Default admin password | `admin123` |
| `DEFAULT_MAX_GOALS` | Max goals per new user | `5` |
| `SECRET_KEY` | Flask session secret (change in production!) | — |
| `DEBUG` | Debug mode | `True` |
| `HOST` | Server bind address | `0.0.0.0` |
| `PORT` | Server port | `5000` |
### Production Deployment
1. Change `SECRET_KEY` to a random secure string
2. Set `DEBUG = False`
3. Change default admin credentials
4. Use a production WSGI server (e.g., gunicorn):
1. Copy `config.example.py` to `config.py`
2. Change `SECRET_KEY` to a random secure string
3. Set `DEBUG = False`
4. Change default admin credentials
5. Use a production WSGI server (e.g., gunicorn):
```bash
uv add gunicorn
@ -81,9 +85,11 @@ uv run gunicorn -w 4 -b 0.0.0.0:5000 app:app
```
GoalsBreakDown/
├── app.py # Flask application
├── config.py # Configuration
├── database.py # TinyDB operations
├── config.example.py # Configuration template (tracked in git)
├── config.py # Local configuration (NOT tracked in git)
├── database.py # SQLite operations
├── auth.py # Authentication helpers
├── schema.py # Database schema & migration
├── templates/ # HTML templates
├── static/
│ ├── css/ # Stylesheets
@ -94,8 +100,9 @@ GoalsBreakDown/
## Tech Stack
- **Backend:** Python + Flask
- **Database:** TinyDB
- **Database:** SQLite
- **Frontend:** Vanilla JS + HTML/CSS
- **Drag-and-Drop:** SortableJS
- **Markdown:** marked.js
- **Authentication:** bcrypt + Flask sessions
- **Environment:** uv

View File

@ -107,7 +107,7 @@ async function openNote(noteId) {
document.getElementById("note-modal").classList.add("active");
}
function openNewNote() {
async function openNewNote() {
editingNoteId = null;
document.getElementById("note-modal-title").textContent = "New Note";
document.getElementById("note-id").value = "";
@ -120,7 +120,11 @@ function openNewNote() {
document.getElementById("note-modal").classList.add("active");
if (savedGoalId) {
populateTasks(savedGoalId);
await populateTasks(savedGoalId);
const goal = goals.find(g => g.id === savedGoalId);
if (goal && goal.selected_task_id) {
document.getElementById("note-task").value = goal.selected_task_id;
}
} else {
document.getElementById("note-task").innerHTML = '<option value="">None</option>';
}
@ -225,10 +229,7 @@ document.addEventListener("DOMContentLoaded", () => {
populateTasks(goalId);
});
document.getElementById("filter-goal").addEventListener("change", async () => {
const goalId = parseInt(document.getElementById("filter-goal").value) || null;
savedGoalId = goalId;
await patch("/api/user/selected-goal", { goal_id: goalId });
document.getElementById("filter-goal").addEventListener("change", () => {
loadNotes();
});