Initial commit: LMS microservices scaffold

- Gradle multi-module: libs/{common-http,security-jwt,markdown,test-support},
  services/{identity,academic,progress,gateway}, web (Thymeleaf+htmx)
- identity-service: JWT auth (RSA), accounts, students, groups, enrollment,
  import from XLSX/CSV, credentials export, password reset
- academic-service: lab import from JSON/zip (markdown + YAML + variants),
  levels, API
- progress-service: schema for student levels and variant assignments
- gateway: Spring Cloud Gateway, JWT validation, route propagation
- web: Server-side UI with flex/footer layout, light/dark theme toggle,
  admin dashboard, groups CRUD, student portal placeholder
- docker-compose: PostgreSQL 18
- Tests: identity smoke test with Testcontainers
This commit is contained in:
2026-09-10 19:46:13 +03:00
commit 9b7748571e
119 changed files with 4671 additions and 0 deletions

View File

@@ -0,0 +1,37 @@
server:
port: ${SERVER_PORT:8082}
spring:
application:
name: academic-service
datasource:
url: ${LMS_DB_URL:jdbc:postgresql://localhost:5432/lms?currentSchema=academic}
username: ${LMS_DB_USER:lms}
password: ${LMS_DB_PASSWORD:lms}
jpa:
open-in-view: false
hibernate:
ddl-auto: validate
properties:
hibernate:
default_schema: academic
flyway:
enabled: true
locations: classpath:db/migration
schemas: academic
default-schema: academic
create-schemas: true
management:
endpoints:
web:
exposure:
include: health,info
endpoint:
health:
probes:
enabled: true
springdoc:
api-docs:
path: /v3/api-docs

View File

@@ -0,0 +1,2 @@
-- Baseline for the academic schema (courses, articles, labs, levels, variants).
CREATE SCHEMA IF NOT EXISTS academic;

View File

@@ -0,0 +1,88 @@
-- Courses, articles, semesters, labs, levels, variants for the academic service.
CREATE TABLE courses (
id uuid PRIMARY KEY DEFAULT uuidv7(),
title varchar(200) NOT NULL,
summary text NOT NULL DEFAULT '',
position integer NOT NULL DEFAULT 0,
published boolean NOT NULL DEFAULT false,
created_at timestamptz NOT NULL DEFAULT now()
);
CREATE TABLE articles (
id uuid PRIMARY KEY DEFAULT uuidv7(),
course_id uuid NOT NULL REFERENCES courses(id) ON DELETE CASCADE,
title varchar(200) NOT NULL,
slug varchar(200) NOT NULL,
summary text NOT NULL DEFAULT '',
body_md text NOT NULL DEFAULT '',
status varchar(16) NOT NULL DEFAULT 'draft',
position integer NOT NULL DEFAULT 0,
published_at timestamptz,
created_by uuid,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
CONSTRAINT articles_course_slug_unique UNIQUE (course_id, slug)
);
CREATE TABLE article_assets (
id uuid PRIMARY KEY DEFAULT uuidv7(),
article_id uuid REFERENCES articles(id) ON DELETE CASCADE,
filename varchar(255) NOT NULL,
stored_path varchar(512) NOT NULL,
mime varchar(128) NOT NULL DEFAULT '',
size bigint NOT NULL DEFAULT 0,
created_at timestamptz NOT NULL DEFAULT now()
);
CREATE TABLE semesters (
id uuid PRIMARY KEY DEFAULT uuidv7(),
title varchar(100) NOT NULL,
year integer NOT NULL,
active boolean NOT NULL DEFAULT false
);
CREATE TABLE labs (
id uuid PRIMARY KEY DEFAULT uuidv7(),
number integer NOT NULL,
title varchar(200) NOT NULL,
semester_id uuid NOT NULL REFERENCES semesters(id) ON DELETE RESTRICT,
description_md text NOT NULL DEFAULT '',
deadline_at timestamptz,
defense_at timestamptz,
points_max numeric(4,1) NOT NULL DEFAULT 5,
status varchar(16) NOT NULL DEFAULT 'draft',
created_at timestamptz NOT NULL DEFAULT now(),
CONSTRAINT labs_number_semester_unique UNIQUE (number, semester_id)
);
CREATE TABLE lab_levels (
id uuid PRIMARY KEY DEFAULT uuidv7(),
lab_id uuid NOT NULL REFERENCES labs(id) ON DELETE CASCADE,
code varchar(32) NOT NULL,
title varchar(100) NOT NULL,
description text NOT NULL DEFAULT '',
position integer NOT NULL DEFAULT 0,
points_max numeric(4,1),
CONSTRAINT lab_levels_lab_code_unique UNIQUE (lab_id, code)
);
CREATE TABLE variants (
id uuid PRIMARY KEY DEFAULT uuidv7(),
lab_id uuid NOT NULL REFERENCES labs(id) ON DELETE CASCADE,
lab_level_id uuid NOT NULL REFERENCES lab_levels(id) ON DELETE CASCADE,
number varchar(32) NOT NULL,
title varchar(200) NOT NULL DEFAULT '',
statement_md text NOT NULL DEFAULT '',
position integer NOT NULL DEFAULT 0,
CONSTRAINT variants_lab_level_number_unique UNIQUE (lab_id, lab_level_id, number)
);
CREATE TABLE variant_examples (
id uuid PRIMARY KEY DEFAULT uuidv7(),
variant_id uuid NOT NULL REFERENCES variants(id) ON DELETE CASCADE,
position integer NOT NULL DEFAULT 0,
input text NOT NULL DEFAULT '',
output text NOT NULL DEFAULT '',
explanation text NOT NULL DEFAULT ''
);

View File

@@ -0,0 +1,4 @@
-- Seed data: default semester for development.
INSERT INTO semesters (id, title, year, active)
VALUES ('00000000-0000-7000-8000-000000000001', 'осень', 2026, true)
ON CONFLICT DO NOTHING;