Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0ba96c47a8 | |||
| 0f93f1ab76 | |||
| 7c238701b7 | |||
| c5c8b15e75 | |||
| 9bc3c7c8fc | |||
| 0476315322 |
@@ -0,0 +1,44 @@
|
||||
name: Claude Code Review
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, ready_for_review, reopened]
|
||||
# Optional: Only run on specific file changes
|
||||
# paths:
|
||||
# - "src/**/*.ts"
|
||||
# - "src/**/*.tsx"
|
||||
# - "src/**/*.js"
|
||||
# - "src/**/*.jsx"
|
||||
|
||||
jobs:
|
||||
claude-review:
|
||||
# Optional: Filter by PR author
|
||||
# if: |
|
||||
# github.event.pull_request.user.login == 'external-contributor' ||
|
||||
# github.event.pull_request.user.login == 'new-developer' ||
|
||||
# github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR'
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
issues: read
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Run Claude Code Review
|
||||
id: claude-review
|
||||
uses: anthropics/claude-code-action@v1
|
||||
with:
|
||||
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
||||
plugin_marketplaces: 'https://github.com/anthropics/claude-code.git'
|
||||
plugins: 'code-review@claude-code-plugins'
|
||||
prompt: '/code-review:code-review ${{ github.repository }}/pull/${{ github.event.pull_request.number }}'
|
||||
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
|
||||
# or https://code.claude.com/docs/en/cli-reference for available options
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
name: Claude Code
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
pull_request_review_comment:
|
||||
types: [created]
|
||||
issues:
|
||||
types: [opened, assigned]
|
||||
pull_request_review:
|
||||
types: [submitted]
|
||||
|
||||
jobs:
|
||||
claude:
|
||||
if: |
|
||||
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
|
||||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
|
||||
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
|
||||
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
issues: read
|
||||
id-token: write
|
||||
actions: read # Required for Claude to read CI results on PRs
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Run Claude Code
|
||||
id: claude
|
||||
uses: anthropics/claude-code-action@v1
|
||||
with:
|
||||
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
||||
|
||||
# This is an optional setting that allows Claude to read CI results on PRs
|
||||
additional_permissions: |
|
||||
actions: read
|
||||
|
||||
# Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it.
|
||||
# prompt: 'Update the pull request description to include a summary of changes.'
|
||||
|
||||
# Optional: Add claude_args to customize behavior and configuration
|
||||
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
|
||||
# or https://code.claude.com/docs/en/cli-reference for available options
|
||||
# claude_args: '--allowed-tools Bash(gh pr:*)'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<img src="logoExcaliDash.png" alt="ExcaliDash Logo" width="80" height="88">
|
||||
|
||||
# ExcaliDash v0.1.8
|
||||
# ExcaliDash
|
||||
|
||||

|
||||

|
||||
|
||||
+14
@@ -27,3 +27,17 @@ CSRF Protection (8a78b2b)
|
||||
- Updated docker-compose configurations with new environment variables
|
||||
- E2E test suite improvements and reliability fixes
|
||||
- Added Kubernetes deployment note in README
|
||||
|
||||
### Kubernetes
|
||||
|
||||
A `CSRF_SECRET` environment variable is now required for CSRF protection. Generate a secure 32+ character random string:
|
||||
|
||||
```bash
|
||||
openssl rand -base64 32
|
||||
|
||||
Add it to your deployment:
|
||||
- Docker Compose: Add CSRF_SECRET=<your-secret> to the backend service environment
|
||||
- Kubernetes: Add to your ConfigMap/Secret and reference in the backend deployment
|
||||
|
||||
If not set, the backend will refuse to start.
|
||||
```
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "backend",
|
||||
"version": "0.2.1",
|
||||
"version": "0.3.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -532,7 +532,6 @@ export const validateImportedDrawing = (data: any): boolean => {
|
||||
// CSRF Protection
|
||||
// ============================================================================
|
||||
|
||||
const CSRF_TOKEN_LENGTH = 32;
|
||||
const CSRF_TOKEN_HEADER = "x-csrf-token";
|
||||
const CSRF_TOKEN_EXPIRY_MS = 24 * 60 * 60 * 1000; // 24 hours
|
||||
const CSRF_TOKEN_FUTURE_SKEW_MS = 5 * 60 * 1000; // 5 minutes clock skew tolerance
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "frontend",
|
||||
"private": true,
|
||||
"version": "0.2.1",
|
||||
"version": "0.3.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -48,7 +48,7 @@ export const UploadProvider: React.FC<{ children: ReactNode }> = ({ children })
|
||||
|
||||
const uploadFiles = useCallback(async (files: File[], targetCollectionId: string | null) => {
|
||||
const newTasks: UploadTask[] = files.map(f => ({
|
||||
id: Math.random().toString(36).substring(2, 11),
|
||||
id: crypto.randomUUID(),
|
||||
fileName: f.name,
|
||||
status: 'pending',
|
||||
progress: 0
|
||||
@@ -56,12 +56,12 @@ export const UploadProvider: React.FC<{ children: ReactNode }> = ({ children })
|
||||
|
||||
setTasks(prev => [...newTasks, ...prev]);
|
||||
|
||||
// Map file names to task IDs for progress callbacks
|
||||
const fileTaskMap = new Map<string, string>();
|
||||
newTasks.forEach(t => fileTaskMap.set(t.fileName, t.id));
|
||||
// Map file index to task ID for progress callbacks (handles duplicate filenames)
|
||||
const indexToTaskId = new Map<number, string>();
|
||||
newTasks.forEach((t, index) => indexToTaskId.set(index, t.id));
|
||||
|
||||
const handleProgress = (fileName: string, status: UploadStatus, progress: number, error?: string) => {
|
||||
const taskId = fileTaskMap.get(fileName);
|
||||
const handleProgress = (fileIndex: number, status: UploadStatus, progress: number, error?: string) => {
|
||||
const taskId = indexToTaskId.get(fileIndex);
|
||||
if (taskId) {
|
||||
updateTask(taskId, { status, progress, error });
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ export const importDrawings = async (
|
||||
targetCollectionId: string | null,
|
||||
onSuccess?: () => void | Promise<void>,
|
||||
onProgress?: (
|
||||
fileName: string,
|
||||
fileIndex: number,
|
||||
status: UploadStatus,
|
||||
progress: number,
|
||||
error?: string
|
||||
@@ -25,12 +25,20 @@ export const importDrawings = async (
|
||||
let failCount = 0;
|
||||
const errors: string[] = [];
|
||||
|
||||
// Build a map from drawingFile index to original file index for progress reporting
|
||||
const originalIndexMap = new Map<number, number>();
|
||||
drawingFiles.forEach((df, i) => {
|
||||
const originalIndex = files.indexOf(df);
|
||||
originalIndexMap.set(i, originalIndex);
|
||||
});
|
||||
|
||||
// We process files in parallel (Promise.all) but we could limit concurrency if needed.
|
||||
// For now, full parallel is fine as browser limits connection count anyway.
|
||||
await Promise.all(
|
||||
drawingFiles.map(async (file) => {
|
||||
drawingFiles.map(async (file, drawingIndex) => {
|
||||
const fileIndex = originalIndexMap.get(drawingIndex) ?? drawingIndex;
|
||||
try {
|
||||
if (onProgress) onProgress(file.name, 'processing', 0); // Parsing phase
|
||||
if (onProgress) onProgress(fileIndex, 'processing', 0); // Parsing phase
|
||||
|
||||
const text = await file.text();
|
||||
const data = JSON.parse(text);
|
||||
@@ -61,7 +69,7 @@ export const importDrawings = async (
|
||||
preview: svg.outerHTML,
|
||||
};
|
||||
|
||||
if (onProgress) onProgress(file.name, 'uploading', 0);
|
||||
if (onProgress) onProgress(fileIndex, 'uploading', 0);
|
||||
|
||||
await api.post("/drawings", payload, {
|
||||
headers: {
|
||||
@@ -73,12 +81,12 @@ export const importDrawings = async (
|
||||
const percentCompleted = Math.round(
|
||||
(progressEvent.loaded * 100) / progressEvent.total
|
||||
);
|
||||
onProgress(file.name, 'uploading', percentCompleted);
|
||||
onProgress(fileIndex, 'uploading', percentCompleted);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
if (onProgress) onProgress(file.name, 'success', 100);
|
||||
if (onProgress) onProgress(fileIndex, 'success', 100);
|
||||
successCount++;
|
||||
|
||||
} catch (err: any) {
|
||||
@@ -90,7 +98,7 @@ export const importDrawings = async (
|
||||
err?.message ||
|
||||
"Upload failed";
|
||||
errors.push(`${file.name}: ${errorMessage}`);
|
||||
if (onProgress) onProgress(file.name, 'error', 0, errorMessage);
|
||||
if (onProgress) onProgress(fileIndex, 'error', 0, errorMessage);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
@@ -15,19 +15,16 @@ try {
|
||||
console.warn("Unable to read VERSION file:", error);
|
||||
}
|
||||
|
||||
if (
|
||||
!process.env.VITE_APP_VERSION ||
|
||||
process.env.VITE_APP_VERSION.trim().length === 0
|
||||
) {
|
||||
process.env.VITE_APP_VERSION = versionFromFile;
|
||||
if (!process.env.VITE_APP_BUILD_LABEL) {
|
||||
process.env.VITE_APP_BUILD_LABEL = "local development build";
|
||||
}
|
||||
}
|
||||
const appVersion = process.env.VITE_APP_VERSION?.trim() || versionFromFile;
|
||||
const buildLabel = process.env.VITE_APP_BUILD_LABEL?.trim() || "local development build";
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
define: {
|
||||
'import.meta.env.VITE_APP_VERSION': JSON.stringify(appVersion),
|
||||
'import.meta.env.VITE_APP_BUILD_LABEL': JSON.stringify(buildLabel),
|
||||
},
|
||||
server: {
|
||||
proxy: {
|
||||
"/api": {
|
||||
|
||||
Reference in New Issue
Block a user