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:
parent
3c325bdb0f
commit
2629e8f2cd
3
.gitignore
vendored
3
.gitignore
vendored
@ -23,6 +23,9 @@ uv.lock
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
|
||||||
|
# Configuration (each developer must create their own)
|
||||||
|
config.py
|
||||||
|
|
||||||
# Environment variables
|
# Environment variables
|
||||||
.env
|
.env
|
||||||
.env.local
|
.env.local
|
||||||
|
|||||||
55
README.md
55
README.md
@ -26,6 +26,9 @@ A web-based task management application focused on goal-oriented task tracking w
|
|||||||
git clone <repository-url>
|
git clone <repository-url>
|
||||||
cd GoalsBreakDown
|
cd GoalsBreakDown
|
||||||
|
|
||||||
|
# Create your local configuration from the example
|
||||||
|
cp config.example.py config.py
|
||||||
|
|
||||||
# Install dependencies with uv
|
# Install dependencies with uv
|
||||||
uv sync
|
uv sync
|
||||||
|
|
||||||
@ -44,32 +47,33 @@ The application will start at **http://127.0.0.1:5000**
|
|||||||
|
|
||||||
## Configuration
|
## 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
|
```bash
|
||||||
# Database path
|
# Copy the example config and edit it
|
||||||
DB_PATH = "data/db.json"
|
cp config.example.py config.py
|
||||||
|
|
||||||
# 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
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
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
|
### Production Deployment
|
||||||
|
|
||||||
1. Change `SECRET_KEY` to a random secure string
|
1. Copy `config.example.py` to `config.py`
|
||||||
2. Set `DEBUG = False`
|
2. Change `SECRET_KEY` to a random secure string
|
||||||
3. Change default admin credentials
|
3. Set `DEBUG = False`
|
||||||
4. Use a production WSGI server (e.g., gunicorn):
|
4. Change default admin credentials
|
||||||
|
5. Use a production WSGI server (e.g., gunicorn):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
uv add gunicorn
|
uv add gunicorn
|
||||||
@ -81,9 +85,11 @@ uv run gunicorn -w 4 -b 0.0.0.0:5000 app:app
|
|||||||
```
|
```
|
||||||
GoalsBreakDown/
|
GoalsBreakDown/
|
||||||
├── app.py # Flask application
|
├── app.py # Flask application
|
||||||
├── config.py # Configuration
|
├── config.example.py # Configuration template (tracked in git)
|
||||||
├── database.py # TinyDB operations
|
├── config.py # Local configuration (NOT tracked in git)
|
||||||
|
├── database.py # SQLite operations
|
||||||
├── auth.py # Authentication helpers
|
├── auth.py # Authentication helpers
|
||||||
|
├── schema.py # Database schema & migration
|
||||||
├── templates/ # HTML templates
|
├── templates/ # HTML templates
|
||||||
├── static/
|
├── static/
|
||||||
│ ├── css/ # Stylesheets
|
│ ├── css/ # Stylesheets
|
||||||
@ -94,8 +100,9 @@ GoalsBreakDown/
|
|||||||
## Tech Stack
|
## Tech Stack
|
||||||
|
|
||||||
- **Backend:** Python + Flask
|
- **Backend:** Python + Flask
|
||||||
- **Database:** TinyDB
|
- **Database:** SQLite
|
||||||
- **Frontend:** Vanilla JS + HTML/CSS
|
- **Frontend:** Vanilla JS + HTML/CSS
|
||||||
- **Drag-and-Drop:** SortableJS
|
- **Drag-and-Drop:** SortableJS
|
||||||
|
- **Markdown:** marked.js
|
||||||
- **Authentication:** bcrypt + Flask sessions
|
- **Authentication:** bcrypt + Flask sessions
|
||||||
- **Environment:** uv
|
- **Environment:** uv
|
||||||
|
|||||||
@ -107,7 +107,7 @@ async function openNote(noteId) {
|
|||||||
document.getElementById("note-modal").classList.add("active");
|
document.getElementById("note-modal").classList.add("active");
|
||||||
}
|
}
|
||||||
|
|
||||||
function openNewNote() {
|
async function openNewNote() {
|
||||||
editingNoteId = null;
|
editingNoteId = null;
|
||||||
document.getElementById("note-modal-title").textContent = "New Note";
|
document.getElementById("note-modal-title").textContent = "New Note";
|
||||||
document.getElementById("note-id").value = "";
|
document.getElementById("note-id").value = "";
|
||||||
@ -120,7 +120,11 @@ function openNewNote() {
|
|||||||
document.getElementById("note-modal").classList.add("active");
|
document.getElementById("note-modal").classList.add("active");
|
||||||
|
|
||||||
if (savedGoalId) {
|
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 {
|
} else {
|
||||||
document.getElementById("note-task").innerHTML = '<option value="">None</option>';
|
document.getElementById("note-task").innerHTML = '<option value="">None</option>';
|
||||||
}
|
}
|
||||||
@ -225,10 +229,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
populateTasks(goalId);
|
populateTasks(goalId);
|
||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById("filter-goal").addEventListener("change", async () => {
|
document.getElementById("filter-goal").addEventListener("change", () => {
|
||||||
const goalId = parseInt(document.getElementById("filter-goal").value) || null;
|
|
||||||
savedGoalId = goalId;
|
|
||||||
await patch("/api/user/selected-goal", { goal_id: goalId });
|
|
||||||
loadNotes();
|
loadNotes();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user