Розробка LMS-платформи з підтримкою LTI-інтеграцій

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Розробка LMS-платформи з підтримкою LTI-інтеграцій
Складна
від 2 тижнів до 3 місяців
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Підтримка LTI-інтеграцій у LMS

LTI (Learning Tools Interoperability) — стандарт IMS Global для вбудовування зовнішніх освітніх інструментів в LMS. Через LTI викладач налаштовує Kahoot, Phet Simulations, Coursera for Campus, Microsoft Teams один раз — і студенти запускають їх прямо з LMS без окремої реєстрації.

Версії LTI

  • LTI 1.1 — застарілий, підпис OAuth 1.0. Все ще використовується багатьма постачальниками.
  • LTI 1.3 — сучасний стандарт, OAuth 2.0 + OpenID Connect. Обов'язковий для нових інтеграцій.

LTI 1.3 — Platform Role (Ваша LMS)

Ваша LMS виступає як Platform (IMS-термінологія). Зовнішній інструмент — Tool. Процес:

  1. Користувач клікає на LTI-ссилку в LMS
  2. LMS ініціює OIDC Login Request до Tool
  3. Tool відповідає Auth Request
  4. LMS створює підписаний JWT і POST-ом відправляє на Tool
  5. Tool валідує JWT через JWKS LMS
import { Provider } from 'ltijs'; // npm install ltijs

// Налаштування LMS як LTI Platform
Provider.setup(
  process.env.LTI_ENCRYPTION_KEY!,  // 32+ символів для шифрування cookies
  {
    url: process.env.DATABASE_URL!,
    plugin: require('ltijs-postgresql'),  // адаптер PostgreSQL
  },
  {
    cookies: { secure: true, sameSite: 'None' },
    devMode: process.env.NODE_ENV !== 'production',
  }
);

// Реєстрація зовнішнього інструменту
await Provider.registerPlatform({
  url: 'https://tool.example.com',
  name: 'Kahoot Integration',
  clientId: process.env.KAHOOT_CLIENT_ID!,
  authenticationEndpoint: 'https://tool.example.com/lti/auth',
  accesstokenEndpoint: 'https://tool.example.com/lti/token',
  authConfig: {
    method: 'JWK_SET',
    key: 'https://tool.example.com/.well-known/jwks.json',
  },
});

// Обробник запуску інструменту
Provider.onConnect(async (token, req, res) => {
  const { email, name, role } = token.userInfo;
  const contextId = token.platformContext.context?.id;

  // Перевірити/створити користувача в зовнішньому інструменті
  res.json({ token: token.jwt });
});

await Provider.deploy({ serverless: true });

LTI 1.1 — для застарілих інструментів

Деякі постачальники (Phet, певні тести) все ще використовують LTI 1.1 з підписом OAuth 1.0:

import oauth from 'oauth-signature';

function launchLti11(
  launchUrl: string,
  consumerKey: string,
  consumerSecret: string,
  params: Record<string, string>
): { url: string; method: 'POST'; body: string } {
  const baseParams: Record<string, string> = {
    lti_message_type: 'basic-lti-launch-request',
    lti_version: 'LTI-1p0',
    oauth_callback: 'about:blank',
    oauth_consumer_key: consumerKey,
    oauth_nonce: crypto.randomUUID().replace(/-/g, ''),
    oauth_signature_method: 'HMAC-SHA1',
    oauth_timestamp: String(Math.floor(Date.now() / 1000)),
    oauth_version: '1.0',
    ...params,
  };

  const signature = oauth.generate('POST', launchUrl, baseParams, consumerSecret, '');
  baseParams.oauth_signature = signature;

  const body = new URLSearchParams(baseParams).toString();
  return { url: launchUrl, method: 'POST', body };
}

// Сторінка запуску інструменту
app.get('/courses/:courseId/tools/:toolId/launch', authenticate, async (req, res) => {
  const tool = await db.ltiTools.findById(req.params.toolId);
  const enrollment = await db.enrollments.findByCourseAndUser(
    req.params.courseId, req.user.id
  );

  if (tool.version === '1.1') {
    const launch = launchLti11(tool.launch_url, tool.consumer_key, tool.consumer_secret, {
      resource_link_id: `${req.params.courseId}-${req.params.toolId}`,
      resource_link_title: tool.name,
      user_id: req.user.id,
      lis_person_name_full: req.user.name,
      lis_person_contact_email_primary: req.user.email,
      roles: enrollment.role === 'instructor' ? 'Instructor' : 'Student',
      context_id: req.params.courseId,
      context_title: enrollment.courseTitle,
    });

    // Авто-сабмит форму через HTML
    res.send(`
      <!DOCTYPE html>
      <html>
        <body>
          <form id="lti" method="POST" action="${launch.url}">
            ${Object.entries(Object.fromEntries(new URLSearchParams(launch.body)))
              .map(([k, v]) => `<input type="hidden" name="${k}" value="${v}" />`)
              .join('\n')}
          </form>
          <script>document.getElementById('lti').submit();</script>
        </body>
      </html>
    `);
  }
});

Зберігання конфігурації інструментів

CREATE TABLE lti_tools (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  course_id UUID REFERENCES courses(id),
  name VARCHAR(255) NOT NULL,
  version VARCHAR(10) NOT NULL,  -- '1.1' | '1.3'
  -- LTI 1.1
  launch_url TEXT,
  consumer_key VARCHAR(255),
  consumer_secret TEXT,
  -- LTI 1.3
  client_id VARCHAR(255),
  platform_id VARCHAR(255),
  deployment_id VARCHAR(255),
  oidc_auth_url TEXT,
  jwks_url TEXT,
  -- Налаштування
  open_in_new_tab BOOLEAN DEFAULT false,
  custom_params JSONB DEFAULT '{}',
  created_at TIMESTAMPTZ DEFAULT now()
);

Отримання результатів через LTI Outcomes (1.1)

// Інструмент може відправити оцінку назад у LMS
app.post('/lti/grade', async (req, res) => {
  const { sourcedId, score, action } = parseLtiOutcomesXml(req.body);

  // sourcedId містить userId та resourceLinkId
  const [userId, resourceId] = parseLisResultSourcedId(sourcedId);

  await db.ltiGrades.upsert({
    userId,
    resourceId,
    score: Number(score),
    receivedAt: new Date(),
  });

  // Оновити прогрес курсу
  await updateCourseProgress(userId, resourceId, Number(score));

  res.type('application/xml').send(`
    <?xml version="1.0" encoding="UTF-8"?>
    <imsx_POXEnvelopeResponse xmlns="http://www.imsglobal.org/services/ltiv1p1/xsd/imsoms_v1p0">
      <imsx_POXHeader>
        <imsx_POXResponseHeaderInfo>
          <imsx_version>V1.0</imsx_version>
          <imsx_messageIdentifier>${crypto.randomUUID()}</imsx_messageIdentifier>
          <imsx_statusInfo>
            <imsx_codeMajor>success</imsx_codeMajor>
            <imsx_severity>status</imsx_severity>
          </imsx_statusInfo>
        </imsx_POXResponseHeaderInfo>
      </imsx_POXHeader>
      <imsx_POXBody><replaceResultResponse /></imsx_POXBody>
    </imsx_POXEnvelopeResponse>
  `);
});

Строки виконання

LTI 1.1 інтеграція (Consumer + Launch + Grades) — 3–5 днів. LTI 1.3 з OIDC flow через ltijs — 1 тиждень. Підтримка обох + UI управління інструментами — 2 тижні.