Enhance session restoration and add user file viewer endpoint

This commit is contained in:
Leon Bösche
2026-01-10 01:39:15 +01:00
parent 1ceb27dea8
commit cadf504643
3 changed files with 64 additions and 7 deletions

View File

@@ -52,12 +52,13 @@ class MainApp extends StatefulWidget {
class _MainAppState extends State<MainApp> {
final _sessionBloc = SessionBloc();
late final AuthBloc _authBloc;
late final Future<void> _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<MainApp> {
ActivityBloc(ActivityApi(ApiClient(_sessionBloc))),
),
],
child: MaterialApp.router(
routerConfig: _router,
theme: AppTheme.darkTheme,
child: FutureBuilder<void>(
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,
);
},
),
);
}

View File

@@ -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),
);
}

View File

@@ -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)