Skip to content

Commit 7f47996

Browse files
committed
Merge feature/api-separation: API separation with Azure Table Storage and ID string refactor
2 parents aa9a552 + 247f1ed commit 7f47996

44 files changed

Lines changed: 6126 additions & 614 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# API Configuration
2+
VITE_API_URL=http://localhost:7071/api
3+
VITE_API_KEY=your-shared-key-here

.github/workflows/azure-static-web-apps-agreeable-pebble-0216f2f1e.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,16 @@ jobs:
3535
- name: Build And Deploy
3636
id: builddeploy
3737
uses: Azure/static-web-apps-deploy@v1
38+
env:
39+
VITE_API_URL: /api
40+
VITE_API_KEY: ${{ secrets.API_SHARED_KEY }}
3841
with:
3942
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_AGREEABLE_PEBBLE_0216F2F1E }}
4043
action: "upload"
4144
###### Repository/Build Configurations - These values can be configured to match your app requirements. ######
4245
# For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig
43-
app_location: "/" # App source code path
44-
api_location: "" # Api source code path - optional
46+
app_location: "/" # App source code path (frontend)
47+
api_location: "api" # Api source code path (Azure Functions API)
4548
output_location: "dist" # Built app content directory - optional
4649
github_id_token: ${{ steps.idtoken.outputs.result }}
4750
###### End of Repository/Build Configurations ######
@@ -55,4 +58,5 @@ jobs:
5558
id: closepullrequest
5659
uses: Azure/static-web-apps-deploy@v1
5760
with:
61+
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_AGREEABLE_PEBBLE_0216F2F1E }}
5862
action: "close"

.github/workflows/tests.yml

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
name: Tests
2+
3+
on:
4+
push:
5+
branches: [ main, feature/** ]
6+
pull_request:
7+
branches: [ main ]
8+
9+
jobs:
10+
frontend-tests:
11+
name: Frontend Tests
12+
runs-on: ubuntu-latest
13+
steps:
14+
- uses: actions/checkout@v3
15+
16+
- name: Setup Node.js
17+
uses: actions/setup-node@v3
18+
with:
19+
node-version: '18'
20+
cache: 'npm'
21+
22+
- name: Install dependencies
23+
run: npm ci
24+
25+
- name: Run tests
26+
run: npm test -- --run --reporter=verbose
27+
env:
28+
VITE_API_URL: /api
29+
VITE_API_KEY: test-key
30+
31+
api-tests:
32+
name: API Tests
33+
runs-on: ubuntu-latest
34+
steps:
35+
- uses: actions/checkout@v3
36+
37+
- name: Setup Node.js
38+
uses: actions/setup-node@v3
39+
with:
40+
node-version: '18'
41+
cache: 'npm'
42+
cache-dependency-path: api/package-lock.json
43+
44+
- name: Install dependencies
45+
run: |
46+
cd api
47+
npm ci
48+
49+
- name: Run tests
50+
run: |
51+
cd api
52+
npm test -- --run --reporter=verbose
53+
env:
54+
AZURE_STORAGE_CONNECTION_STRING: UseDevelopmentStorage=true
55+
API_SHARED_KEY: test-key

MIGRATION.md

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
# Migration Guide: IndexedDB to Azure Table Storage
2+
3+
This guide explains how to migrate from the previous IndexedDB-based version to the new API-based architecture.
4+
5+
## What Changed
6+
7+
### Before (v1.x)
8+
- Data stored client-side in browser IndexedDB
9+
- Single-user per browser
10+
- No server-side component
11+
- Dexie.js for database access
12+
13+
### After (v2.0)
14+
- Data stored server-side in Azure Table Storage
15+
- Multi-user support with userId partitioning
16+
- RESTful API with Shared Key authentication
17+
- API client wraps HTTP calls
18+
19+
## Architecture Changes
20+
21+
```
22+
Before:
23+
[Browser] → [IndexedDB]
24+
25+
After:
26+
[Browser] → [API] → [Azure Table Storage]
27+
28+
Shared Key Auth
29+
```
30+
31+
## Key Differences
32+
33+
1. **Authentication:**
34+
- UI: Still uses Azure SWA (Microsoft SSO) - **no changes**
35+
- API: New Shared Key authentication via `x-api-key` header
36+
37+
2. **Data Storage:**
38+
- Moved from client-side (IndexedDB) to server-side (Table Storage)
39+
- Each user's data is partitioned by `userId`
40+
41+
3. **IDs:**
42+
- IndexedDB used auto-increment integers
43+
- Table Storage uses string-based RowKeys
44+
- API converts between them for frontend compatibility
45+
46+
## Breaking Changes
47+
48+
- `db.ts` API is mostly compatible, but uses HTTP under the hood
49+
50+
## Migration Steps
51+
52+
### For Developers
53+
54+
1. **Update environment configuration:**
55+
56+
Create `.env`:
57+
```env
58+
VITE_API_URL=http://localhost:7071/api
59+
VITE_API_KEY=dev-shared-key-123
60+
```
61+
62+
Create `api/local.settings.json`:
63+
```json
64+
{
65+
"IsEncrypted": false,
66+
"Values": {
67+
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
68+
"FUNCTIONS_WORKER_RUNTIME": "node",
69+
"AZURE_STORAGE_CONNECTION_STRING": "UseDevelopmentStorage=true",
70+
"API_SHARED_KEY": "dev-shared-key-123"
71+
}
72+
}
73+
```
74+
75+
2. **Install dependencies:**
76+
```bash
77+
npm install
78+
cd api && npm install && cd ..
79+
```
80+
81+
3. **Start Azurite:**
82+
```bash
83+
azurite --silent --location ./azurite
84+
```
85+
86+
4. **Create tables:**
87+
```bash
88+
az storage table create --name routines --connection-string "UseDevelopmentStorage=true"
89+
az storage table create --name exercises --connection-string "UseDevelopmentStorage=true"
90+
```
91+
92+
5. **Run API and UI:**
93+
```bash
94+
# Terminal 1
95+
cd api && npm start
96+
97+
# Terminal 2
98+
npm run dev
99+
```
100+
101+
### For Users
102+
103+
**Data Migration Options:**
104+
105+
#### Option 1: Manual Re-entry
106+
If you have limited data, the simplest approach is to re-enter it in the new version.
107+
108+
#### Option 2: Export/Import Script
109+
Create a migration script to export from IndexedDB and import via API:
110+
111+
```javascript
112+
// export-data.js - Run in browser console on OLD version
113+
async function exportData() {
114+
const { db } = await import('./src/db.ts');
115+
116+
const routines = await db.routines.toArray();
117+
const exercises = await db.exercises.toArray();
118+
const exportData = {\n routines,\n exercises\n };
119+
120+
// Download as JSON
121+
const blob = new Blob([JSON.stringify(exportData, null, 2)], { type: 'application/json' });
122+
const url = URL.createObjectURL(blob);
123+
const a = document.createElement('a');
124+
a.href = url;
125+
a.download = 'pump-export.json';
126+
a.click();
127+
}
128+
129+
function blobToBase64(blob) {
130+
return new Promise((resolve) => {
131+
const reader = new FileReader();
132+
reader.onloadend = () => {
133+
resolve(reader.result.split(',')[1]);
134+
};
135+
reader.readAsDataURL(blob);
136+
});
137+
}
138+
139+
exportData();
140+
```
141+
142+
```javascript
143+
// import-data.js - Run via Node.js with NEW version API
144+
const fs = require('fs');
145+
const fetch = require('node-fetch');
146+
147+
const API_URL = 'http://localhost:7071/api';
148+
const API_KEY = 'dev-shared-key-123';
149+
const USER_ID = 'your-user-id'; // Get from Azure SWA auth
150+
151+
async function importData(filePath) {
152+
const data = JSON.parse(fs.readFileSync(filePath, 'utf8'));
153+
154+
const headers = {
155+
'Content-Type': 'application/json',
156+
'x-api-key': API_KEY
157+
};
158+
159+
// Import routines
160+
const routineIdMap = new Map();
161+
for (const routine of data.routines) {
162+
const res = await fetch(`${API_URL}/routines?userId=${USER_ID}`, {
163+
method: 'POST',
164+
headers,
165+
body: JSON.stringify({
166+
date: routine.date,
167+
name: routine.name,
168+
order: routine.order,
169+
userId: USER_ID
170+
})
171+
});
172+
const created = await res.json();
173+
routineIdMap.set(routine.id, created.id);
174+
}
175+
176+
// Import exercises
177+
const exerciseIdMap = new Map();
178+
for (const exercise of data.exercises) {
179+
const newRoutineId = routineIdMap.get(exercise.routineId);
180+
const res = await fetch(`${API_URL}/exercises?userId=${USER_ID}`, {
181+
method: 'POST',
182+
headers,
183+
body: JSON.stringify({
184+
...exercise,
185+
routineId: newRoutineId,
186+
userId: USER_ID
187+
})
188+
});
189+
const created = await res.json();
190+
exerciseIdMap.set(exercise.id, created.id);
191+
}
192+
193+
console.log('Import complete!');
194+
}
195+
196+
importData('pump-export.json');
197+
```
198+
199+
## Rollback
200+
201+
To rollback to the previous version:
202+
203+
```bash
204+
git checkout save-x # or whichever branch had the old version
205+
```
206+
207+
The old version will continue to work with its IndexedDB data unchanged.
208+
209+
## Production Deployment
210+
211+
See main [README.md](README.md) for Azure deployment instructions.
212+
213+
Key considerations:
214+
- Use strong Shared Keys in production
215+
- Configure CORS properly on Function App
216+
- Set appropriate Table Storage retention policies
217+
- Monitor API usage and costs
218+
219+
## Support
220+
221+
If you encounter issues during migration:
222+
1. Check browser console for errors
223+
2. Check Function App logs in Azure Portal
224+
3. Verify API_KEY matches between frontend and backend
225+
4. Ensure tables exist in Storage Account
226+

0 commit comments

Comments
 (0)