Enhance session restoration and add user file viewer endpoint
This commit is contained in:
@@ -52,12 +52,13 @@ class MainApp extends StatefulWidget {
|
|||||||
class _MainAppState extends State<MainApp> {
|
class _MainAppState extends State<MainApp> {
|
||||||
final _sessionBloc = SessionBloc();
|
final _sessionBloc = SessionBloc();
|
||||||
late final AuthBloc _authBloc;
|
late final AuthBloc _authBloc;
|
||||||
|
late final Future<void> _restoreFuture;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
// Restore session from persistent storage early so ApiClient has token if present
|
// 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
|
// Configure DI to use HTTP repositories
|
||||||
configureDependencies(_sessionBloc);
|
configureDependencies(_sessionBloc);
|
||||||
|
|
||||||
@@ -78,9 +79,22 @@ class _MainAppState extends State<MainApp> {
|
|||||||
ActivityBloc(ActivityApi(ApiClient(_sessionBloc))),
|
ActivityBloc(ActivityApi(ApiClient(_sessionBloc))),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
child: MaterialApp.router(
|
child: FutureBuilder<void>(
|
||||||
routerConfig: _router,
|
future: _restoreFuture,
|
||||||
theme: AppTheme.darkTheme,
|
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,
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -168,11 +168,14 @@ class FileService {
|
|||||||
String orgId,
|
String orgId,
|
||||||
String fileId,
|
String fileId,
|
||||||
) async {
|
) async {
|
||||||
if (orgId.isEmpty || fileId.isEmpty) {
|
if (fileId.isEmpty) {
|
||||||
throw Exception('OrgId and fileId cannot be empty');
|
throw Exception('fileId cannot be empty');
|
||||||
}
|
}
|
||||||
|
final path = orgId.isEmpty
|
||||||
|
? '/user/files/$fileId/view'
|
||||||
|
: '/orgs/$orgId/files/$fileId/view';
|
||||||
return await _apiClient.get(
|
return await _apiClient.get(
|
||||||
'/orgs/$orgId/files/$fileId/view',
|
path,
|
||||||
fromJson: (data) => ViewerSession.fromJson(data),
|
fromJson: (data) => ViewerSession.fromJson(data),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
r.Get("/user/files", func(w http.ResponseWriter, req *http.Request) {
|
||||||
userFilesHandler(w, req, db)
|
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
|
// Download user file
|
||||||
r.Get("/user/files/download", func(w http.ResponseWriter, req *http.Request) {
|
r.Get("/user/files/download", func(w http.ResponseWriter, req *http.Request) {
|
||||||
downloadUserFileHandler(w, req, db, storageClient)
|
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)
|
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) {
|
func editorHandler(w http.ResponseWriter, r *http.Request, db *database.DB, auditLogger *audit.Logger) {
|
||||||
userIDStr, _ := middleware.GetUserID(r.Context())
|
userIDStr, _ := middleware.GetUserID(r.Context())
|
||||||
userID, _ := uuid.Parse(userIDStr)
|
userID, _ := uuid.Parse(userIDStr)
|
||||||
|
|||||||
Reference in New Issue
Block a user