@vue-skuilder/db 0.1.11-7 → 0.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/dist/core/index.d.mts +5 -5
  2. package/dist/core/index.d.ts +5 -5
  3. package/dist/core/index.js +212 -50
  4. package/dist/core/index.js.map +1 -1
  5. package/dist/core/index.mjs +212 -50
  6. package/dist/core/index.mjs.map +1 -1
  7. package/dist/{dataLayerProvider-DqtNroSh.d.ts → dataLayerProvider-VieuAAkV.d.mts} +8 -1
  8. package/dist/{dataLayerProvider-BInqI_RF.d.mts → dataLayerProvider-juuqUHOP.d.ts} +8 -1
  9. package/dist/impl/couch/index.d.mts +19 -7
  10. package/dist/impl/couch/index.d.ts +19 -7
  11. package/dist/impl/couch/index.js +229 -63
  12. package/dist/impl/couch/index.js.map +1 -1
  13. package/dist/impl/couch/index.mjs +228 -62
  14. package/dist/impl/couch/index.mjs.map +1 -1
  15. package/dist/impl/static/index.d.mts +13 -6
  16. package/dist/impl/static/index.d.ts +13 -6
  17. package/dist/impl/static/index.js +142 -46
  18. package/dist/impl/static/index.js.map +1 -1
  19. package/dist/impl/static/index.mjs +142 -46
  20. package/dist/impl/static/index.mjs.map +1 -1
  21. package/dist/{index-CLL31bEy.d.ts → index-CWY6yhkV.d.ts} +1 -1
  22. package/dist/{index-CUNnL38E.d.mts → index-DZyxHCcf.d.mts} +1 -1
  23. package/dist/index.d.mts +28 -20
  24. package/dist/index.d.ts +28 -20
  25. package/dist/index.js +374 -108
  26. package/dist/index.js.map +1 -1
  27. package/dist/index.mjs +378 -108
  28. package/dist/index.mjs.map +1 -1
  29. package/dist/pouch/index.d.mts +1 -0
  30. package/dist/pouch/index.d.ts +1 -0
  31. package/dist/pouch/index.js +49 -0
  32. package/dist/pouch/index.js.map +1 -0
  33. package/dist/pouch/index.mjs +16 -0
  34. package/dist/pouch/index.mjs.map +1 -0
  35. package/dist/{types-DC-ckZug.d.mts → types-Che4wTwA.d.mts} +1 -1
  36. package/dist/{types-BefDGkKa.d.ts → types-DtoI27Xh.d.ts} +1 -1
  37. package/dist/{types-legacy-Birv-Jx6.d.mts → types-legacy-B8ahaCbj.d.mts} +5 -1
  38. package/dist/{types-legacy-Birv-Jx6.d.ts → types-legacy-B8ahaCbj.d.ts} +5 -1
  39. package/dist/{userDB-C33Hzjgn.d.mts → userDB-B7zTQ123.d.ts} +88 -55
  40. package/dist/{userDB-DusL7OXe.d.ts → userDB-DJ8HMw83.d.mts} +88 -55
  41. package/dist/util/packer/index.d.mts +3 -3
  42. package/dist/util/packer/index.d.ts +3 -3
  43. package/package.json +3 -3
  44. package/src/core/interfaces/contentSource.ts +3 -2
  45. package/src/core/interfaces/courseDB.ts +26 -3
  46. package/src/core/interfaces/dataLayerProvider.ts +9 -1
  47. package/src/core/interfaces/userDB.ts +80 -64
  48. package/src/core/navigators/elo.ts +10 -7
  49. package/src/core/navigators/index.ts +1 -1
  50. package/src/core/types/types-legacy.ts +5 -0
  51. package/src/impl/common/BaseUserDB.ts +45 -3
  52. package/src/impl/couch/CouchDBSyncStrategy.ts +2 -2
  53. package/src/impl/couch/PouchDataLayerProvider.ts +21 -0
  54. package/src/impl/couch/adminDB.ts +2 -2
  55. package/src/impl/couch/auth.ts +13 -4
  56. package/src/impl/couch/classroomDB.ts +10 -12
  57. package/src/impl/couch/courseAPI.ts +2 -2
  58. package/src/impl/couch/courseDB.ts +130 -11
  59. package/src/impl/couch/courseLookupDB.ts +4 -3
  60. package/src/impl/couch/index.ts +36 -4
  61. package/src/impl/couch/pouchdb-setup.ts +3 -3
  62. package/src/impl/couch/updateQueue.ts +51 -33
  63. package/src/impl/static/StaticDataLayerProvider.ts +11 -0
  64. package/src/impl/static/courseDB.ts +47 -8
  65. package/src/pouch/index.ts +2 -0
  66. package/src/study/SessionController.ts +168 -51
  67. package/src/study/SpacedRepetition.ts +1 -1
  68. package/tsup.config.ts +1 -0
@@ -1,6 +1,6 @@
1
1
  import { CourseConfig, CourseElo, DataShape, SkuilderCourseData } from '@vue-skuilder/common';
2
2
  import { StudySessionNewItem, StudySessionItem } from './contentSource';
3
- import { TagStub, Tag } from '../types/types-legacy';
3
+ import { TagStub, Tag, QualifiedCardID } from '../types/types-legacy';
4
4
  import { DataLayerResult } from '../types/db';
5
5
  import { NavigationStrategyManager } from './navigationStrategyManager';
6
6
 
@@ -53,7 +53,16 @@ export interface CourseDBInterface extends NavigationStrategyManager {
53
53
  /**
54
54
  * Get cards sorted by ELO rating
55
55
  */
56
- getCardsByELO(elo: number, limit?: number): Promise<string[]>;
56
+ getCardsByELO(
57
+ elo: number,
58
+ limit?: number
59
+ ): Promise<
60
+ {
61
+ courseID: string;
62
+ cardID: string;
63
+ elo?: number;
64
+ }[]
65
+ >;
57
66
 
58
67
  /**
59
68
  * Get ELO data for specific cards
@@ -75,7 +84,7 @@ export interface CourseDBInterface extends NavigationStrategyManager {
75
84
  */
76
85
  getCardsCenteredAtELO(
77
86
  options: { limit: number; elo: 'user' | 'random' | number },
78
- filter?: (id: string) => boolean
87
+ filter?: (card: QualifiedCardID) => boolean
79
88
  ): Promise<StudySessionItem[]>;
80
89
 
81
90
  /**
@@ -136,4 +145,18 @@ export interface CourseDBInterface extends NavigationStrategyManager {
136
145
  elo: CourseElo;
137
146
  }[]
138
147
  >;
148
+
149
+ /**
150
+ * Search for cards by text content
151
+ * @param query Text to search for
152
+ * @returns Array of matching card data
153
+ */
154
+ searchCards(query: string): Promise<any[]>;
155
+
156
+ /**
157
+ * Find documents using PouchDB query syntax
158
+ * @param request PouchDB find request
159
+ * @returns Query response
160
+ */
161
+ find(request: PouchDB.Find.FindRequest<any>): Promise<PouchDB.Find.FindResponse<any>>;
139
162
  }
@@ -1,6 +1,6 @@
1
1
  // db/src/core/interfaces.ts
2
2
 
3
- import { UserDBInterface } from './userDB';
3
+ import { UserDBInterface, UserDBReader } from './userDB';
4
4
  import { CourseDBInterface, CoursesDBInterface } from './courseDB';
5
5
  import { ClassroomDBInterface } from './classroomDB';
6
6
  import { AdminDBInterface } from './adminDB';
@@ -14,6 +14,14 @@ export interface DataLayerProvider {
14
14
  */
15
15
  getUserDB(): UserDBInterface;
16
16
 
17
+ /**
18
+ * Create a UserDBReader for a specific user (admin access required)
19
+ * Uses session authentication to verify requesting user is admin
20
+ * @param targetUsername - The username to create a reader for
21
+ * @throws Error if requesting user is not 'admin'
22
+ */
23
+ createUserReaderForUser(targetUsername: string): Promise<UserDBReader>;
24
+
17
25
  /**
18
26
  * Get a course database interface
19
27
  */
@@ -6,46 +6,16 @@ import {
6
6
  } from '@db/core/types/user';
7
7
  import { CourseElo, Status } from '@vue-skuilder/common';
8
8
  import { Moment } from 'moment';
9
- import { CardHistory, CardRecord } from '../types/types-legacy';
9
+ import { CardHistory, CardRecord, QualifiedCardID } from '../types/types-legacy';
10
10
  import { UserConfig } from '../types/user';
11
11
  import { DocumentUpdater } from '@db/study';
12
12
 
13
13
  /**
14
- * User data and authentication
14
+ * Read-only user data operations
15
15
  */
16
- export interface UserDBInterface extends DocumentUpdater {
17
- /**
18
- * Create a new user account
19
- */
20
- createAccount(
21
- username: string,
22
- password: string
23
- ): Promise<{
24
- status: Status;
25
- error: string;
26
- }>;
27
-
28
- /**
29
- * Log in as a user
30
- */
31
- login(
32
- username: string,
33
- password: string
34
- ): Promise<{
35
- ok: boolean;
36
- name?: string;
37
- roles?: string[];
38
- }>;
39
-
40
- /**
41
- * Log out the current user
42
- */
43
- logout(): Promise<{
44
- ok: boolean;
45
- }>;
46
-
16
+ export interface UserDBReader {
17
+ get<T>(id: string): Promise<T & PouchDB.Core.RevisionIdMeta>;
47
18
  getUsername(): string;
48
-
49
19
  isLoggedIn(): boolean;
50
20
 
51
21
  /**
@@ -53,16 +23,6 @@ export interface UserDBInterface extends DocumentUpdater {
53
23
  */
54
24
  getConfig(): Promise<UserConfig>;
55
25
 
56
- /**
57
- * Update user configuration
58
- */
59
- setConfig(config: Partial<UserConfig>): Promise<void>;
60
-
61
- /**
62
- * Record a user's interaction with a card
63
- */
64
- putCardRecord<T extends CardRecord>(record: T): Promise<CardHistory<CardRecord>>;
65
-
66
26
  /**
67
27
  * Get cards that the user has seen
68
28
  */
@@ -71,17 +31,7 @@ export interface UserDBInterface extends DocumentUpdater {
71
31
  /**
72
32
  * Get cards that are actively scheduled for review
73
33
  */
74
- getActiveCards(): Promise<string[]>;
75
-
76
- /**
77
- * Register user for a course
78
- */
79
- registerForCourse(courseId: string, previewMode?: boolean): Promise<PouchDB.Core.Response>;
80
-
81
- /**
82
- * Drop a course registration
83
- */
84
- dropCourse(courseId: string, dropStatus?: string): Promise<PouchDB.Core.Response>;
34
+ getActiveCards(): Promise<QualifiedCardID[]>;
85
35
 
86
36
  /**
87
37
  * Get user's course registrations
@@ -106,6 +56,43 @@ export interface UserDBInterface extends DocumentUpdater {
106
56
 
107
57
  getActivityRecords(): Promise<ActivityRecord[]>;
108
58
 
59
+ /**
60
+ * Get user's classroom registrations
61
+ */
62
+ getUserClassrooms(): Promise<ClassroomRegistrationDoc>;
63
+
64
+ /**
65
+ * Get user's active classes
66
+ */
67
+ getActiveClasses(): Promise<string[]>;
68
+
69
+ getCourseInterface(courseId: string): Promise<UsrCrsDataInterface>;
70
+ }
71
+
72
+ /**
73
+ * User data mutation operations
74
+ */
75
+ export interface UserDBWriter extends DocumentUpdater {
76
+ /**
77
+ * Update user configuration
78
+ */
79
+ setConfig(config: Partial<UserConfig>): Promise<void>;
80
+
81
+ /**
82
+ * Record a user's interaction with a card
83
+ */
84
+ putCardRecord<T extends CardRecord>(record: T): Promise<CardHistory<CardRecord>>;
85
+
86
+ /**
87
+ * Register user for a course
88
+ */
89
+ registerForCourse(courseId: string, previewMode?: boolean): Promise<PouchDB.Core.Response>;
90
+
91
+ /**
92
+ * Drop a course registration
93
+ */
94
+ dropCourse(courseId: string, dropStatus?: string): Promise<PouchDB.Core.Response>;
95
+
109
96
  /**
110
97
  * Schedule a card for review
111
98
  */
@@ -137,28 +124,57 @@ export interface UserDBInterface extends DocumentUpdater {
137
124
  dropFromClassroom(classId: string): Promise<PouchDB.Core.Response>;
138
125
 
139
126
  /**
140
- * Get user's classroom registrations
127
+ * Update user's ELO rating for a course
141
128
  */
142
- getUserClassrooms(): Promise<ClassroomRegistrationDoc>;
129
+ updateUserElo(courseId: string, elo: CourseElo): Promise<PouchDB.Core.Response>;
143
130
 
144
131
  /**
145
- * Get user's active classes
132
+ * Reset all user data (progress, registrations, etc.) while preserving authentication
146
133
  */
147
- getActiveClasses(): Promise<string[]>;
134
+ resetUserData(): Promise<{ status: Status; error?: string }>;
135
+ }
148
136
 
137
+ /**
138
+ * Authentication and account management operations
139
+ */
140
+ export interface UserDBAuthenticator {
149
141
  /**
150
- * Update user's ELO rating for a course
142
+ * Create a new user account
151
143
  */
152
- updateUserElo(courseId: string, elo: CourseElo): Promise<PouchDB.Core.Response>;
144
+ createAccount(
145
+ username: string,
146
+ password: string
147
+ ): Promise<{
148
+ status: Status;
149
+ error: string;
150
+ }>;
153
151
 
154
152
  /**
155
- * Reset all user data (progress, registrations, etc.) while preserving authentication
153
+ * Log in as a user
156
154
  */
157
- resetUserData(): Promise<{ status: Status; error?: string }>;
155
+ login(
156
+ username: string,
157
+ password: string
158
+ ): Promise<{
159
+ ok: boolean;
160
+ name?: string;
161
+ roles?: string[];
162
+ }>;
158
163
 
159
- getCourseInterface(courseId: string): Promise<UsrCrsDataInterface>;
164
+ /**
165
+ * Log out the current user
166
+ */
167
+ logout(): Promise<{
168
+ ok: boolean;
169
+ }>;
160
170
  }
161
171
 
172
+ /**
173
+ * Complete user database interface - combines all user operations
174
+ * This maintains backward compatibility with existing code
175
+ */
176
+ export interface UserDBInterface extends UserDBReader, UserDBWriter, UserDBAuthenticator {}
177
+
162
178
  export interface UserCourseSettings {
163
179
  [setting: string]: string | number | boolean;
164
180
  }
@@ -3,7 +3,7 @@ import { CourseDBInterface } from '../interfaces/courseDB';
3
3
  import { UserDBInterface } from '../interfaces/userDB';
4
4
  import { ContentNavigator } from './index';
5
5
  import { CourseElo } from '@vue-skuilder/common';
6
- import { StudySessionReviewItem, StudySessionNewItem } from '..';
6
+ import { StudySessionReviewItem, StudySessionNewItem, QualifiedCardID } from '..';
7
7
 
8
8
  export default class ELONavigator extends ContentNavigator {
9
9
  user: UserDBInterface;
@@ -59,13 +59,16 @@ export default class ELONavigator extends ContentNavigator {
59
59
  async getNewCards(limit: number = 99): Promise<StudySessionNewItem[]> {
60
60
  const activeCards = await this.user.getActiveCards();
61
61
  return (
62
- await this.course.getCardsCenteredAtELO({ limit: limit, elo: 'user' }, (c: string) => {
63
- if (activeCards.some((ac) => c.includes(ac))) {
64
- return false;
65
- } else {
66
- return true;
62
+ await this.course.getCardsCenteredAtELO(
63
+ { limit: limit, elo: 'user' },
64
+ (c: QualifiedCardID) => {
65
+ if (activeCards.some((ac) => c.cardID === ac.cardID)) {
66
+ return false;
67
+ } else {
68
+ return true;
69
+ }
67
70
  }
68
- })
71
+ )
69
72
  ).map((c) => {
70
73
  return {
71
74
  ...c,
@@ -32,7 +32,7 @@ export abstract class ContentNavigator implements StudyContentSource {
32
32
  let NavigatorImpl;
33
33
 
34
34
  // Try different extension variations
35
- const variations = ['', '.js', '.ts'];
35
+ const variations = ['.ts', '.js', ''];
36
36
 
37
37
  for (const ext of variations) {
38
38
  try {
@@ -21,6 +21,11 @@ export enum DocType {
21
21
  NAVIGATION_STRATEGY = 'NAVIGATION_STRATEGY',
22
22
  }
23
23
 
24
+ export interface QualifiedCardID {
25
+ courseID: string;
26
+ cardID: string;
27
+ }
28
+
24
29
  /**
25
30
  * Interface for all data on course content and pedagogy stored
26
31
  * in the c/pouch database.
@@ -217,6 +217,10 @@ Currently logged-in as ${this._username}.`
217
217
  return ret;
218
218
  }
219
219
 
220
+ public async get<T>(id: string): Promise<T & PouchDB.Core.RevisionIdMeta> {
221
+ return this.localDB.get<T>(id);
222
+ }
223
+
220
224
  public update<T extends PouchDB.Core.Document<object>>(id: string, update: Update<T>) {
221
225
  return this.updateQueue.update(id, update);
222
226
  }
@@ -273,7 +277,12 @@ Currently logged-in as ${this._username}.`
273
277
  include_docs: true,
274
278
  });
275
279
 
276
- return reviews.rows.map((r) => `${r.doc!.courseId}-${r.doc!.cardId}`);
280
+ return reviews.rows.map((r) => {
281
+ return {
282
+ courseID: r.doc!.courseId,
283
+ cardID: r.doc!.cardId,
284
+ };
285
+ });
277
286
  }
278
287
 
279
288
  public async getActivityRecords(): Promise<ActivityRecord[]> {
@@ -620,8 +629,18 @@ Currently logged-in as ${this._username}.`
620
629
  this.setDBandQ();
621
630
 
622
631
  this.syncStrategy.startSync(this.localDB, this.remoteDB);
623
- void this.applyDesignDocs();
624
- void this.deduplicateReviews();
632
+ this.applyDesignDocs().catch((error) => {
633
+ log(`Error in applyDesignDocs background task: ${error}`);
634
+ if (error && typeof error === 'object') {
635
+ log(`Full error details in applyDesignDocs: ${JSON.stringify(error)}`);
636
+ }
637
+ });
638
+ this.deduplicateReviews().catch((error) => {
639
+ log(`Error in deduplicateReviews background task: ${error}`);
640
+ if (error && typeof error === 'object') {
641
+ log(`Full error details in background task: ${JSON.stringify(error)}`);
642
+ }
643
+ });
625
644
  BaseUser._initialized = true;
626
645
  }
627
646
 
@@ -641,12 +660,18 @@ Currently logged-in as ${this._username}.`
641
660
  ];
642
661
 
643
662
  private async applyDesignDocs() {
663
+ log(`Starting applyDesignDocs for user: ${this._username}`);
664
+ log(`Remote DB name: ${this.remoteDB.name || 'unknown'}`);
665
+
644
666
  if (this._username === 'admin') {
645
667
  // Skip admin user
668
+ log('Skipping design docs for admin user');
646
669
  return;
647
670
  }
648
671
 
672
+ log(`Applying ${BaseUser.designDocs.length} design docs`);
649
673
  for (const doc of BaseUser.designDocs) {
674
+ log(`Applying design doc: ${doc._id}`);
650
675
  try {
651
676
  // Try to get existing doc
652
677
  try {
@@ -759,6 +784,8 @@ Currently logged-in as ${this._username}.`
759
784
  private async deduplicateReviews() {
760
785
  try {
761
786
  log('Starting deduplication of scheduled reviews...');
787
+ log(`Remote DB name: ${this.remoteDB.name || 'unknown'}`);
788
+ log(`Write DB name: ${this.writeDB.name || 'unknown'}`);
762
789
  /**
763
790
  * Maps the qualified-id of a scheduled review card to
764
791
  * the docId of the same scheduled review.
@@ -770,6 +797,9 @@ Currently logged-in as ${this._username}.`
770
797
  const reviewsMap: { [index: string]: string } = {};
771
798
  const duplicateDocIds: string[] = [];
772
799
 
800
+ log(
801
+ `Attempting to query remoteDB for reviewCards/reviewCards. Database: ${this.remoteDB.name || 'unknown'}`
802
+ );
773
803
  const scheduledReviews = await this.remoteDB.query<{
774
804
  id: string;
775
805
  value: string;
@@ -817,6 +847,18 @@ Currently logged-in as ${this._username}.`
817
847
  }
818
848
  } catch (error) {
819
849
  log(`Error during review deduplication: ${error}`);
850
+ if (error && typeof error === 'object' && 'status' in error && error.status === 404) {
851
+ log(
852
+ `Database not found (404) during review deduplication. Database: ${this.remoteDB.name || 'unknown'}`
853
+ );
854
+ log(
855
+ `This might indicate the user database doesn't exist or the reviewCards view isn't available`
856
+ );
857
+ }
858
+ // Log full error details for debugging
859
+ if (error && typeof error === 'object') {
860
+ log(`Full error details: ${JSON.stringify(error)}`);
861
+ }
820
862
  }
821
863
  }
822
864
 
@@ -8,7 +8,7 @@ import type { SyncStrategy } from '../common/SyncStrategy';
8
8
  import type { AccountCreationResult, AuthenticationResult } from '../common/types';
9
9
  import { getLocalUserDB, hexEncode, updateGuestAccountExpirationDate } from '../common';
10
10
  import pouch from './pouchdb-setup';
11
- import { pouchDBincludeCredentialsConfig } from './index';
11
+ import { createPouchDBConfig } from './index';
12
12
  import { getLoggedInUsername } from './auth';
13
13
 
14
14
  const log = (s: any) => {
@@ -207,7 +207,7 @@ export class CouchDBSyncStrategy implements SyncStrategy {
207
207
  // see: https://github.com/pouchdb-community/pouchdb-authentication/issues/239
208
208
  const ret = new pouch(
209
209
  ENV.COUCHDB_SERVER_PROTOCOL + '://' + ENV.COUCHDB_SERVER_URL + dbName,
210
- pouchDBincludeCredentialsConfig
210
+ createPouchDBConfig()
211
211
  );
212
212
 
213
213
  if (guestAccount) {
@@ -7,6 +7,7 @@ import {
7
7
  CourseDBInterface,
8
8
  DataLayerProvider,
9
9
  UserDBInterface,
10
+ UserDBReader,
10
11
  } from '../../core/interfaces';
11
12
  import { logger } from '../../util/logger';
12
13
  import { initializeDataDirectory } from '../../util/dataDirectory';
@@ -107,6 +108,26 @@ export class CouchDataLayerProvider implements DataLayerProvider {
107
108
  return new AdminDB();
108
109
  }
109
110
 
111
+ async createUserReaderForUser(targetUsername: string): Promise<UserDBReader> {
112
+ // Security check: only admin can access other users' data
113
+ const requestingUsername = await getLoggedInUsername();
114
+ if (requestingUsername !== 'admin') {
115
+ throw new Error('Unauthorized: Only admin users can access other users\' data');
116
+ }
117
+
118
+ logger.info(`Admin user '${requestingUsername}' requesting UserDBReader for '${targetUsername}'`);
119
+
120
+ // Create a new sync strategy for the target user
121
+ const syncStrategy = new CouchDBSyncStrategy();
122
+
123
+ // Create a BaseUser instance for the target user
124
+ // Note: This creates a read-capable user instance without affecting the current session
125
+ const targetUserDB = await BaseUser.instance(syncStrategy, targetUsername);
126
+
127
+ // Return as UserDBReader (which BaseUser implements since UserDBInterface extends UserDBReader)
128
+ return targetUserDB as UserDBReader;
129
+ }
130
+
110
131
  isReadOnly(): boolean {
111
132
  return false;
112
133
  }
@@ -1,7 +1,7 @@
1
1
  import pouch from './pouchdb-setup';
2
2
  import { ENV } from '@db/factory';
3
3
  import {
4
- pouchDBincludeCredentialsConfig,
4
+ createPouchDBConfig,
5
5
  getStartAndEndKeys,
6
6
  getCredentialledCourseConfig,
7
7
  updateCredentialledCourseConfig,
@@ -21,7 +21,7 @@ export class AdminDB implements AdminDBInterface {
21
21
  // if the user is not an admin
22
22
  this.usersDB = new pouch(
23
23
  ENV.COUCHDB_SERVER_PROTOCOL + '://' + ENV.COUCHDB_SERVER_URL + '_users',
24
- pouchDBincludeCredentialsConfig
24
+ createPouchDBConfig()
25
25
  );
26
26
  }
27
27
 
@@ -39,10 +39,14 @@ export async function getCurrentSession(): Promise<SessionResponse> {
39
39
  try {
40
40
  // Handle case where ENV variables might not be properly set
41
41
  if (ENV.COUCHDB_SERVER_URL === NOT_SET || ENV.COUCHDB_SERVER_PROTOCOL === NOT_SET) {
42
- throw new Error('CouchDB server configuration not properly initialized');
42
+ throw new Error(`CouchDB server configuration not properly initialized. Protocol: "${ENV.COUCHDB_SERVER_PROTOCOL}", URL: "${ENV.COUCHDB_SERVER_URL}"`);
43
43
  }
44
44
 
45
- const url = `${ENV.COUCHDB_SERVER_PROTOCOL}://${ENV.COUCHDB_SERVER_URL}_session`;
45
+ // Ensure URL has proper slash before _session endpoint
46
+ const baseUrl = ENV.COUCHDB_SERVER_URL.endsWith('/')
47
+ ? ENV.COUCHDB_SERVER_URL.slice(0, -1)
48
+ : ENV.COUCHDB_SERVER_URL;
49
+ const url = `${ENV.COUCHDB_SERVER_PROTOCOL}://${baseUrl}/_session`;
46
50
  logger.debug(`Attempting session check at: ${url}`);
47
51
 
48
52
  const response = await fetch(url, {
@@ -57,8 +61,13 @@ export async function getCurrentSession(): Promise<SessionResponse> {
57
61
  const resp: SessionResponse = await response.json();
58
62
  return resp;
59
63
  } catch (error) {
60
- logger.error(`Session check error: ${error}`);
61
- throw new Error(`Session check failed: ${error}`);
64
+ // Use same URL construction logic for error reporting
65
+ const baseUrl = ENV.COUCHDB_SERVER_URL.endsWith('/')
66
+ ? ENV.COUCHDB_SERVER_URL.slice(0, -1)
67
+ : ENV.COUCHDB_SERVER_URL;
68
+ const url = `${ENV.COUCHDB_SERVER_PROTOCOL}://${baseUrl}/_session`;
69
+ logger.error(`Session check error attempting to connect to: ${url} - ${error}`);
70
+ throw new Error(`Session check failed connecting to ${url}: ${error}`);
62
71
  }
63
72
  }
64
73
 
@@ -8,12 +8,7 @@ import { ENV } from '@db/factory';
8
8
  import { logger } from '@db/util/logger';
9
9
  import moment from 'moment';
10
10
  import pouch from './pouchdb-setup';
11
- import {
12
- getCourseDB,
13
- getStartAndEndKeys,
14
- pouchDBincludeCredentialsConfig,
15
- REVIEW_TIME_FORMAT,
16
- } from '.';
11
+ import { getCourseDB, getStartAndEndKeys, createPouchDBConfig, REVIEW_TIME_FORMAT } from '.';
17
12
  import { CourseDB, getTag } from './courseDB';
18
13
 
19
14
  import { UserDBInterface } from '@db/core';
@@ -97,7 +92,7 @@ export class StudentClassroomDB
97
92
  const dbName = `classdb-student-${this._id}`;
98
93
  this._db = new pouch(
99
94
  ENV.COUCHDB_SERVER_PROTOCOL + '://' + ENV.COUCHDB_SERVER_URL + dbName,
100
- pouchDBincludeCredentialsConfig
95
+ createPouchDBConfig()
101
96
  );
102
97
  try {
103
98
  const cfg = await this._db.get<ClassroomConfig>(CLASSROOM_CONFIG);
@@ -181,10 +176,13 @@ export class StudentClassroomDB
181
176
  }
182
177
  }
183
178
 
184
- logger.info(`New Cards from classroom ${this._cfg.name}: ${ret.map((c) => c.qualifiedID)}`);
179
+ logger.info(
180
+ `New Cards from classroom ${this._cfg.name}: ${ret.map((c) => `${c.courseID}-${c.cardID}`)}`
181
+ );
185
182
 
186
183
  return ret.filter((c) => {
187
- if (activeCards.some((ac) => c.qualifiedID.includes(ac))) {
184
+ if (activeCards.some((ac) => c.cardID === ac.cardID)) {
185
+ // [ ] almost certainly broken after removing qualifiedID from StudySessionItem
188
186
  return false;
189
187
  } else {
190
188
  return true;
@@ -209,11 +207,11 @@ export class TeacherClassroomDB extends ClassroomDBBase implements TeacherClassr
209
207
  const stuDbName = `classdb-student-${this._id}`;
210
208
  this._db = new pouch(
211
209
  ENV.COUCHDB_SERVER_PROTOCOL + '://' + ENV.COUCHDB_SERVER_URL + dbName,
212
- pouchDBincludeCredentialsConfig
210
+ createPouchDBConfig()
213
211
  );
214
212
  this._stuDb = new pouch(
215
213
  ENV.COUCHDB_SERVER_PROTOCOL + '://' + ENV.COUCHDB_SERVER_URL + stuDbName,
216
- pouchDBincludeCredentialsConfig
214
+ createPouchDBConfig()
217
215
  );
218
216
  try {
219
217
  return this._db
@@ -297,7 +295,7 @@ export function getClassroomDB(classID: string, version: 'student' | 'teacher'):
297
295
 
298
296
  return new pouch(
299
297
  ENV.COUCHDB_SERVER_PROTOCOL + '://' + ENV.COUCHDB_SERVER_URL + dbName,
300
- pouchDBincludeCredentialsConfig
298
+ createPouchDBConfig()
301
299
  );
302
300
  }
303
301
 
@@ -1,5 +1,5 @@
1
1
  import pouch from './pouchdb-setup';
2
- import { pouchDBincludeCredentialsConfig } from '.';
2
+ import { createPouchDBConfig } from '.';
3
3
  import { ENV } from '@db/factory';
4
4
  // import { DataShape } from '../..base-course/Interfaces/DataShape';
5
5
  import { NameSpacer, ShapeDescriptor } from '@vue-skuilder/common';
@@ -279,6 +279,6 @@ export function getCourseDB(courseID: string): PouchDB.Database {
279
279
  const dbName = `coursedb-${courseID}`;
280
280
  return new pouch(
281
281
  ENV.COUCHDB_SERVER_PROTOCOL + '://' + ENV.COUCHDB_SERVER_URL + dbName,
282
- pouchDBincludeCredentialsConfig
282
+ createPouchDBConfig()
283
283
  );
284
284
  }