Skip to content

Commit e1a5461

Browse files
authored
Express init datalayer (#676)
2 parents 7cd1ed4 + a3ea807 commit e1a5461

File tree

6 files changed

+124
-26
lines changed

6 files changed

+124
-26
lines changed

packages/db/src/factory.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import { DataLayerProvider } from './core/interfaces';
55
interface DBEnv {
66
COUCHDB_SERVER_URL: string; // URL of CouchDB server
77
COUCHDB_SERVER_PROTOCOL: string; // Protocol of CouchDB server (http or https)
8+
COUCHDB_USERNAME?: string;
9+
COUCHDB_PASSWORD?: string;
810
}
911

1012
export const ENV: DBEnv = {
@@ -20,6 +22,8 @@ export interface DataLayerConfig {
2022
localStoragePrefix?: string; // Prefix for IndexedDB storage names
2123
COUCHDB_SERVER_URL?: string;
2224
COUCHDB_SERVER_PROTOCOL?: string;
25+
COUCHDB_USERNAME?: string;
26+
COUCHDB_PASSWORD?: string;
2327
};
2428
}
2529

@@ -41,6 +45,8 @@ export async function initializeDataLayer(config: DataLayerConfig): Promise<Data
4145
}
4246
ENV.COUCHDB_SERVER_PROTOCOL = config.options.COUCHDB_SERVER_PROTOCOL;
4347
ENV.COUCHDB_SERVER_URL = config.options.COUCHDB_SERVER_URL;
48+
ENV.COUCHDB_USERNAME = config.options.COUCHDB_USERNAME;
49+
ENV.COUCHDB_PASSWORD = config.options.COUCHDB_PASSWORD;
4450

4551
// Dynamic import to avoid loading both implementations when only one is needed
4652
const { PouchDataLayerProvider } = await import('./impl/pouch/PouchDataLayerProvider');

packages/db/src/impl/pouch/PouchDataLayerProvider.ts

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,32 @@ export class PouchDataLayerProvider implements DataLayerProvider {
2727
async initialize(): Promise<void> {
2828
if (this.initialized) return;
2929

30-
// Get the current username from session
31-
this.currentUsername = await getLoggedInUsername();
32-
33-
// Create the user db instance
34-
if (this.currentUsername) {
35-
this.userDB = await User.instance(this.currentUsername);
30+
// Check if we are in a Node.js environment
31+
const isNodeEnvironment =
32+
typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
33+
34+
if (isNodeEnvironment) {
35+
console.log(
36+
'PouchDataLayerProvider: Running in Node.js environment, skipping user session check and user DB initialization.'
37+
);
38+
} else {
39+
// Assume browser-like environment, proceed with user session logic
40+
try {
41+
// Get the current username from session
42+
this.currentUsername = await getLoggedInUsername();
43+
44+
// Create the user db instance if a username was found
45+
if (this.currentUsername) {
46+
this.userDB = await User.instance(this.currentUsername);
47+
} else {
48+
console.warn('PouchDataLayerProvider: No logged-in username found in session.');
49+
}
50+
} catch (error) {
51+
console.error(
52+
'PouchDataLayerProvider: Error during user session check or user DB initialization:',
53+
error
54+
);
55+
}
3656
}
3757

3858
this.initialized = true;

packages/db/src/impl/pouch/courseLookupDB.ts

Lines changed: 69 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import pouch from './pouchdb-setup';
2-
// import { ENV } from '@/factory';
2+
import { ENV } from '@/factory';
33

44
const courseLookupDBTitle = 'coursedb-lookup';
55

@@ -13,23 +13,76 @@ interface CourseLookupDoc {
1313
console.log(`COURSELOOKUP FILE RUNNING`);
1414

1515
/**
16-
* a k-v indexes created courses.
17-
*
18-
* k: courseID
19-
* v: course name
16+
* A Lookup table of existant courses. Each docID in this DB correspondes to a
17+
* course database whose name is `coursedb-{docID}`
2018
*/
2119
export default class CourseLookup {
22-
// [ ] this db should be read only for public, admin-write only
23-
// static _db: PouchDB.Database = new pouch(
24-
// ENV.COUCHDB_SERVER_PROTOCOL + '://' + ENV.COUCHDB_SERVER_URL + courseLookupDBTitle,
25-
// {
26-
// // skip_setup: true,
27-
// }
28-
// );
29-
30-
static _db: PouchDB.Database = new pouch('http://localhost:5984/' + courseLookupDBTitle, {
31-
skip_setup: true,
32-
});
20+
// [ ] this db should be read only for public, admin-only for write
21+
// Cache for the PouchDB instance
22+
private static _dbInstance: PouchDB.Database | null = null;
23+
24+
/**
25+
* Static getter for the PouchDB database instance.
26+
* Connects using ENV variables and caches the instance.
27+
* Throws an error if required ENV variables are not set.
28+
*/
29+
private static get _db(): PouchDB.Database {
30+
// Return cached instance if available
31+
if (this._dbInstance) {
32+
return this._dbInstance;
33+
}
34+
35+
// --- Check required environment variables ---
36+
if (ENV.COUCHDB_SERVER_URL === 'NOT_SET' || !ENV.COUCHDB_SERVER_URL) {
37+
throw new Error(
38+
'CourseLookup.db: COUCHDB_SERVER_URL is not set. Ensure initializeDataLayer has been called with valid configuration.'
39+
);
40+
}
41+
if (ENV.COUCHDB_SERVER_PROTOCOL === 'NOT_SET' || !ENV.COUCHDB_SERVER_PROTOCOL) {
42+
throw new Error(
43+
'CourseLookup.db: COUCHDB_SERVER_PROTOCOL is not set. Ensure initializeDataLayer has been called with valid configuration.'
44+
);
45+
}
46+
47+
// --- Construct connection options ---
48+
const dbUrl = `${ENV.COUCHDB_SERVER_PROTOCOL}://${ENV.COUCHDB_SERVER_URL}/${courseLookupDBTitle}`;
49+
const options: PouchDB.Configuration.RemoteDatabaseConfiguration = {
50+
skip_setup: true, // Keep the original option
51+
// fetch: (url, opts) => { // Optional: Add for debugging network requests
52+
// console.log('PouchDB fetch:', url, opts);
53+
// return pouch.fetch(url, opts);
54+
// }
55+
};
56+
57+
// Add authentication if both username and password are provided
58+
if (ENV.COUCHDB_USERNAME && ENV.COUCHDB_PASSWORD) {
59+
options.auth = {
60+
username: ENV.COUCHDB_USERNAME,
61+
password: ENV.COUCHDB_PASSWORD,
62+
};
63+
console.log(`CourseLookup: Connecting to ${dbUrl} with authentication.`);
64+
} else {
65+
console.log(`CourseLookup: Connecting to ${dbUrl} without authentication.`);
66+
}
67+
68+
// --- Create and cache the PouchDB instance ---
69+
try {
70+
this._dbInstance = new pouch(dbUrl, options);
71+
console.log(`CourseLookup: Database instance created for ${courseLookupDBTitle}.`);
72+
return this._dbInstance;
73+
} catch (error) {
74+
console.error(`CourseLookup: Failed to create PouchDB instance for ${dbUrl}`, error);
75+
// Reset cache attempt on failure
76+
this._dbInstance = null;
77+
// Re-throw the error to indicate connection failure
78+
throw new Error(
79+
`CourseLookup: Failed to initialize database connection: ${
80+
error instanceof Error ? error.message : String(error)
81+
}`
82+
);
83+
}
84+
}
85+
3386
/**
3487
* Adds a new course to the lookup database, and returns the courseID
3588
* @param courseName

packages/db/src/impl/pouch/index.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,6 @@ if (isBrowser) {
1717

1818
const expiryDocID: string = 'GuestAccountExpirationDate';
1919

20-
const remoteStr: string = ENV.COUCHDB_SERVER_PROTOCOL + '://' + ENV.COUCHDB_SERVER_URL + 'skuilder';
21-
22-
log(`Remote db: ${remoteStr}`);
23-
2420
const GUEST_LOCAL_DB = `userdb-${GuestUsername}`;
2521
export const localUserDB: PouchDB.Database = new pouch(GUEST_LOCAL_DB);
2622

packages/express/src/client-requests/course-requests.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,11 @@ const elodoc = {
116116
language: 'javascript',
117117
};
118118

119+
/**
120+
* Inserts a design document into a course database.
121+
* @param courseID - The ID of the course database.
122+
* @param doc
123+
*/
119124
function insertDesignDoc(
120125
courseID: string,
121126
doc: {
@@ -168,6 +173,10 @@ type CourseConfig = CreateCourse['data'];
168173
async function createCourse(cfg: CourseConfig): Promise<any> {
169174
cfg.courseID = await CourseLookup.add(cfg.name);
170175

176+
if (!cfg.courseID) {
177+
throw new Error('Course ID not found');
178+
}
179+
171180
const courseDBName: string = getCourseDBName(cfg.courseID);
172181
const dbCreation = await CouchDB.db.create(courseDBName);
173182

packages/express/src/utils/env.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import dotenv from 'dotenv';
22
import process from 'process';
33
import fs from 'fs';
4+
import { initializeDataLayer } from '@vue-skuilder/db';
45

56
dotenv.config({
67
path:
@@ -33,4 +34,17 @@ const env: Env = {
3334
VERSION: getVar('VERSION'),
3435
};
3536

37+
initializeDataLayer({
38+
type: 'pouch',
39+
options: {
40+
COUCHDB_PASSWORD: env.COUCHDB_PASSWORD,
41+
COUCHDB_USERNAME: env.COUCHDB_ADMIN,
42+
COUCHDB_SERVER_PROTOCOL: env.COUCHDB_PROTOCOL,
43+
COUCHDB_SERVER_URL: env.COUCHDB_SERVER,
44+
},
45+
}).catch((e) => {
46+
console.error('Error initializing data layer:', e);
47+
process.exit(1);
48+
});
49+
3650
export default env;

0 commit comments

Comments
 (0)