Skip to content

Refactor: Split Monolithic SqliteApi.ts & SupabaseApi.ts into Domain-Specific Modules#4712

Open
guddu-sutara wants to merge 1 commit into
devfrom
guddu-sde-dev
Open

Refactor: Split Monolithic SqliteApi.ts & SupabaseApi.ts into Domain-Specific Modules#4712
guddu-sutara wants to merge 1 commit into
devfrom
guddu-sde-dev

Conversation

@guddu-sutara

Copy link
Copy Markdown
Contributor

…odules

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@guddu-sutara guddu-sutara changed the title Refactor: Split Monolithic SqliteApi.ts & SupabaseApi.ts into Domain-Specific M… Refactor: Split Monolithic SqliteApi.ts & SupabaseApi.ts into Domain-Specific Modules Jul 14, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces SQLite-first API implementations alongside their Supabase counterparts to support offline data synchronization and local database management. The review feedback highlights several critical SQL injection vulnerabilities across multiple SQLite API files (such as SqliteApi.ops.ts, SqliteApi.assignment.ts, SqliteApi.core.ts, SqliteApi.course.ts, SqliteApi.rewards.ts, and SqliteApi.school.ts) caused by direct string interpolation in SQL queries. It is highly recommended to parameterize these queries using placeholders. Additionally, the feedback advises replacing the boxed String object type with the primitive string type in the course and assignment APIs.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +88 to +95
const query = `SELECT code
FROM ${TABLES.ClassInvite_code}
WHERE class_id='${class_id}'
AND is_deleted = FALSE
AND (expires_at >= '${currentDate}')`;

try {
const res = await this._db?.query(query);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-critical critical

The query is constructed using string interpolation for class_id and currentDate, which introduces a SQL injection vulnerability and can cause runtime errors if the values contain special characters. Please use parameterized queries with ? placeholders instead.

Suggested change
const query = `SELECT code
FROM ${TABLES.ClassInvite_code}
WHERE class_id='${class_id}'
AND is_deleted = FALSE
AND (expires_at >= '${currentDate}')`;
try {
const res = await this._db?.query(query);
const query = `SELECT code
FROM ${TABLES.ClassInvite_code}
WHERE class_id = ?
AND is_deleted = 0
AND (expires_at >= ?)`;
try {
const res = await this._db?.query(query, [class_id, currentDate]);

Comment on lines +127 to +135
const query = `SELECT *
FROM ${TABLES.Result}
WHERE chapter_id = '${chapter_id}'
AND course_id = '${course_id}'
AND class_id ='${classId}'
AND created_at BETWEEN '${startDate}' AND '${endDate}'
ORDER BY created_at DESC;`;

const res = await this._db?.query(query);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-critical critical

This query uses string interpolation for several parameters (chapter_id, course_id, classId, startDate, endDate), making it vulnerable to SQL injection. Please parameterize the query using ? placeholders.

Suggested change
const query = `SELECT *
FROM ${TABLES.Result}
WHERE chapter_id = '${chapter_id}'
AND course_id = '${course_id}'
AND class_id ='${classId}'
AND created_at BETWEEN '${startDate}' AND '${endDate}'
ORDER BY created_at DESC;`;
const res = await this._db?.query(query);
const query = `SELECT *
FROM ${TABLES.Result}
WHERE chapter_id = ?
AND course_id = ?
AND class_id = ?
AND created_at BETWEEN ? AND ?
ORDER BY created_at DESC;`;
const res = await this._db?.query(query, [chapter_id, course_id, classId, startDate, endDate]);

Comment on lines +182 to +192
const query = `
SELECT DISTINCT school.*
FROM ${TABLES.SchoolUser} AS su
JOIN ${TABLES.School} AS school ON su.school_id = school.id
WHERE su.school_id IN (${placeholders})
AND su.user_id = '${userId}'
AND su.role = '${RoleType.AUTOUSER}'
AND su.is_deleted = false;
`;

const res = await this._db?.query(query, schoolIds);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-critical critical

The userId and RoleType.AUTOUSER values are interpolated directly into the query string, which is a security risk. Please parameterize these values along with the schoolIds array.

Suggested change
const query = `
SELECT DISTINCT school.*
FROM ${TABLES.SchoolUser} AS su
JOIN ${TABLES.School} AS school ON su.school_id = school.id
WHERE su.school_id IN (${placeholders})
AND su.user_id = '${userId}'
AND su.role = '${RoleType.AUTOUSER}'
AND su.is_deleted = false;
`;
const res = await this._db?.query(query, schoolIds);
const query = `
SELECT DISTINCT school.*
FROM ${TABLES.SchoolUser} AS su
JOIN ${TABLES.School} AS school ON su.school_id = school.id
WHERE su.school_id IN (${placeholders})
AND su.user_id = ?
AND su.role = ?
AND su.is_deleted = 0;
`;
const res = await this._db?.query(query, [...schoolIds, userId, RoleType.AUTOUSER]);

Comment on lines +1244 to +1278
const latestBatchQuery = `
SELECT a.batch_id
FROM assignment a
LEFT JOIN assignment_user au
ON a.id = au.assignment_id
AND au.is_deleted = false
WHERE a.class_id = '${classId}'
AND a.course_id = '${courseId}'
AND a.type = 'assessment'
AND a.is_deleted = false
AND a.batch_id IS NOT NULL

-- Active time window
AND (
a.starts_at IS NULL
OR a.starts_at = ''
OR datetime(a.starts_at) <= datetime('${nowIso}')
)
AND (
a.ends_at IS NULL
OR a.ends_at = ''
OR datetime(a.ends_at) > datetime('${nowIso}')
)

-- Assigned to this student
AND (
a.is_class_wise = true
OR au.user_id = '${studentId}'
)

ORDER BY a.created_at DESC
LIMIT 1;
`;

const batchRes = await this._db?.query(latestBatchQuery);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-critical critical

This query is constructed using string interpolation for classId, courseId, nowIso, and studentId, which poses a SQL injection risk. Please parameterize the query using ? placeholders and pass the values as arguments to query().

    const latestBatchQuery = `
      SELECT a.batch_id
      FROM assignment a
      LEFT JOIN assignment_user au
        ON a.id = au.assignment_id
        AND au.is_deleted = 0
      WHERE a.class_id = ?
        AND a.course_id = ?
        AND a.type = 'assessment'
        AND a.is_deleted = 0
        AND a.batch_id IS NOT NULL

        -- Active time window
        AND (
          a.starts_at IS NULL
          OR a.starts_at = ''
          OR datetime(a.starts_at) <= datetime(?)
        )
        AND (
          a.ends_at IS NULL
          OR a.ends_at = ''
          OR datetime(a.ends_at) > datetime(?)
        )

        -- Assigned to this student
        AND (
          a.is_class_wise = 1
          OR au.user_id = ?
        )

      ORDER BY a.created_at DESC
      LIMIT 1;
    `;

    const batchRes = await this._db?.query(latestBatchQuery, [
      classId,
      courseId,
      nowIso,
      nowIso,
      studentId,
    ]);

Comment on lines +1734 to +1736
this.executeQuery(
`UPDATE pull_sync_info SET last_pulled = '${formattedTimestamp}' WHERE table_name IN (${tables})`,
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The formattedTimestamp is interpolated directly into the SQL statement. To prevent potential SQL syntax issues and adhere to safe query practices, please parameterize this value using a ? placeholder.

Suggested change
this.executeQuery(
`UPDATE pull_sync_info SET last_pulled = '${formattedTimestamp}' WHERE table_name IN (${tables})`,
);
this.executeQuery(
`UPDATE pull_sync_info SET last_pulled = ? WHERE table_name IN (${tables})`,
[formattedTimestamp],
);

Comment on lines +238 to +244
async updateSoundFlag(userId: string, value: boolean) {
const query = `
UPDATE "user"
SET sfx_off = ${value ? 1 : 0}
WHERE id = "${userId}";
`;
await this.executeQuery(query);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The userId is interpolated directly into the query string. Please parameterize this query to prevent SQL injection and ensure robustness.

  async updateSoundFlag(userId: string, value: boolean) {
    const query = `
    UPDATE "user"
    SET sfx_off = ?
    WHERE id = ?;
  `;
    await this.executeQuery(query, [value ? 1 : 0, userId]);

Comment on lines +13 to +17
const quotedIds = ids.map((id) => `"${id}"`).join(', ');
try {
const res = await this._db?.query(
`SELECT * FROM ${TABLES.Badge} WHERE id IN (${quotedIds})`,
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

Instead of manually quoting and interpolating the IDs into the query string, please use parameterized placeholders (?) to prevent SQL injection and syntax errors.

Suggested change
const quotedIds = ids.map((id) => `"${id}"`).join(', ');
try {
const res = await this._db?.query(
`SELECT * FROM ${TABLES.Badge} WHERE id IN (${quotedIds})`,
);
const placeholders = ids.map(() => '?').join(', ');
try {
const res = await this._db?.query(
`SELECT * FROM ${TABLES.Badge} WHERE id IN (${placeholders})`,
ids,
);

Comment on lines +35 to +44
async getStudentResult(
studentId: string,
fromCache?: boolean,
): Promise<TableTypes<'result'>[]> {
await this.ensureInitialized();
const query = `
SELECT * FROM ${TABLES.Result}
where student_id = '${studentId}'
`;
const res = await this._db?.query(query);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The studentId is interpolated directly into the query string. Please parameterize this query to prevent SQL injection.

Suggested change
async getStudentResult(
studentId: string,
fromCache?: boolean,
): Promise<TableTypes<'result'>[]> {
await this.ensureInitialized();
const query = `
SELECT * FROM ${TABLES.Result}
where student_id = '${studentId}'
`;
const res = await this._db?.query(query);
async getStudentResult(
studentId: string,
fromCache?: boolean,
): Promise<TableTypes<'result'>[]> {
await this.ensureInitialized();
const query = `
SELECT * FROM ${TABLES.Result}
where student_id = ?
`;
const res = await this._db?.query(query, [studentId]);

lessonId: string,
classId?: string,
userId?: string,
): Promise<String | undefined> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Avoid using the boxed String object type as a type annotation. Use the primitive string type instead.

Suggested change
): Promise<String | undefined> {
): Promise<string | undefined> {

);

if (!assessmentLessonIds.length) {
return {} as TableTypes<'subject_lesson'>;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Avoid using the boxed String object type as a type annotation. Use the primitive string type instead.

  ): Promise<string | undefined> {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant