diff --git a/b0esche_cloud/lib/main.dart b/b0esche_cloud/lib/main.dart index b496439..73545d0 100644 --- a/b0esche_cloud/lib/main.dart +++ b/b0esche_cloud/lib/main.dart @@ -52,12 +52,13 @@ class MainApp extends StatefulWidget { class _MainAppState extends State { final _sessionBloc = SessionBloc(); late final AuthBloc _authBloc; + late final Future _restoreFuture; @override void initState() { super.initState(); // Restore session from persistent storage early so ApiClient has token if present - SessionBloc.restoreSession(_sessionBloc); + _restoreFuture = SessionBloc.restoreSession(_sessionBloc); // Configure DI to use HTTP repositories configureDependencies(_sessionBloc); @@ -78,9 +79,22 @@ class _MainAppState extends State { ActivityBloc(ActivityApi(ApiClient(_sessionBloc))), ), ], - child: MaterialApp.router( - routerConfig: _router, - theme: AppTheme.darkTheme, + child: FutureBuilder( + future: _restoreFuture, + builder: (context, snapshot) { + if (snapshot.connectionState != ConnectionState.done) { + return MaterialApp( + theme: AppTheme.darkTheme, + home: const Scaffold( + body: Center(child: CircularProgressIndicator()), + ), + ); + } + return MaterialApp.router( + routerConfig: _router, + theme: AppTheme.darkTheme, + ); + }, ), ); } diff --git a/b0esche_cloud/lib/services/file_service.dart b/b0esche_cloud/lib/services/file_service.dart index ee5f88e..17bd493 100644 --- a/b0esche_cloud/lib/services/file_service.dart +++ b/b0esche_cloud/lib/services/file_service.dart @@ -168,11 +168,14 @@ class FileService { String orgId, String fileId, ) async { - if (orgId.isEmpty || fileId.isEmpty) { - throw Exception('OrgId and fileId cannot be empty'); + if (fileId.isEmpty) { + throw Exception('fileId cannot be empty'); } + final path = orgId.isEmpty + ? '/user/files/$fileId/view' + : '/orgs/$orgId/files/$fileId/view'; return await _apiClient.get( - '/orgs/$orgId/files/$fileId/view', + path, fromJson: (data) => ViewerSession.fromJson(data), ); } diff --git a/go_cloud/internal/http/routes.go b/go_cloud/internal/http/routes.go index 06146ac..b3f0d84 100644 --- a/go_cloud/internal/http/routes.go +++ b/go_cloud/internal/http/routes.go @@ -81,6 +81,10 @@ func NewRouter(cfg *config.Config, db *database.DB, jwtManager *jwt.Manager, aut r.Get("/user/files", func(w http.ResponseWriter, req *http.Request) { userFilesHandler(w, req, db) }) + // User file viewer + r.Get("/user/files/{fileId}/view", func(w http.ResponseWriter, req *http.Request) { + userViewerHandler(w, req, db, auditLogger) + }) // Download user file r.Get("/user/files/download", func(w http.ResponseWriter, req *http.Request) { downloadUserFileHandler(w, req, db, storageClient) @@ -382,6 +386,42 @@ func viewerHandler(w http.ResponseWriter, r *http.Request, db *database.DB, audi json.NewEncoder(w).Encode(session) } +// userViewerHandler serves a viewer session for personal workspace files +func userViewerHandler(w http.ResponseWriter, r *http.Request, db *database.DB, auditLogger *audit.Logger) { + userIDStr, _ := middleware.GetUserID(r.Context()) + userID, _ := uuid.Parse(userIDStr) + fileId := chi.URLParam(r, "fileId") + + // For now, return a synthetic viewer session similar to org viewer + session := struct { + ViewUrl string `json:"viewUrl"` + Capabilities struct { + CanEdit bool `json:"canEdit"` + CanAnnotate bool `json:"canAnnotate"` + IsPdf bool `json:"isPdf"` + } `json:"capabilities"` + ExpiresAt string `json:"expiresAt"` + }{ + ViewUrl: "https://view.example.com/" + fileId, + Capabilities: struct { + CanEdit bool `json:"canEdit"` + CanAnnotate bool `json:"canAnnotate"` + IsPdf bool `json:"isPdf"` + }{ + CanEdit: false, + CanAnnotate: true, + IsPdf: strings.HasSuffix(strings.ToLower(fileId), ".pdf"), + }, + ExpiresAt: time.Now().Add(15 * time.Minute).UTC().Format(time.RFC3339), + } + + // Optionally log activity without org id + db.LogActivity(r.Context(), userID, uuid.Nil, &fileId, "view_user_file", map[string]interface{}{}) + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(session) +} + func editorHandler(w http.ResponseWriter, r *http.Request, db *database.DB, auditLogger *audit.Logger) { userIDStr, _ := middleware.GetUserID(r.Context()) userID, _ := uuid.Parse(userIDStr)