diff --git a/services/progress-service/src/main/kotlin/ru/ysu/lms/progress/SubmissionService.kt b/services/progress-service/src/main/kotlin/ru/ysu/lms/progress/SubmissionService.kt index 80d3feb..71dde36 100644 --- a/services/progress-service/src/main/kotlin/ru/ysu/lms/progress/SubmissionService.kt +++ b/services/progress-service/src/main/kotlin/ru/ysu/lms/progress/SubmissionService.kt @@ -38,6 +38,14 @@ class SubmissionService( val last = submissions.findFirstByStudentIdAndLabIdOrderBySeqDesc(studentId, labId) val seq = (last?.seq ?: 0) + 1 val payload = files.mapIndexed { i, f -> (f.originalFilename ?: "file$i") to f.bytes } + payload.forEach { (name, bytes) -> + if (looksLikeProjectFile(name, bytes)) { + throw BadRequestException( + "Файл «$name» похож на файл проекта (XML), а не на исходник. " + + "Приложите файл .c/.h или zip с проектом.", + ) + } + } val stored = storage.store(studentId, labId, seq, payload) val entity = submissions.save(Submission(studentId = studentId, labId = labId, seq = seq)) return toDto(entity, stored.files) @@ -70,6 +78,22 @@ class SubmissionService( return perStudent.values.map { toDto(it, filesOf(it)) } } + /** Файлы с "исходниковым" расширением, содержимое которых — XML-файл проекта (VS/IDE). */ + private val sourceExtensions = setOf("c", "h", "cpp", "hpp", "cc", "cxx", "txt") + + private fun looksLikeProjectFile(name: String, bytes: ByteArray): Boolean { + val ext = name.substringAfterLast('.', "").lowercase() + if (ext !in sourceExtensions) return false + val head = String(bytes, 0, minOf(bytes.size, 512), Charsets.UTF_8) + .removePrefix("\uFEFF") + .trimStart() + .lowercase() + return head.startsWith(" = storage.listFiles(storage.dirFor(s.studentId, s.labId, s.seq)) diff --git a/web/src/main/kotlin/ru/ysu/lms/web/admin/AdminController.kt b/web/src/main/kotlin/ru/ysu/lms/web/admin/AdminController.kt index 87428ca..5aa4a38 100644 --- a/web/src/main/kotlin/ru/ysu/lms/web/admin/AdminController.kt +++ b/web/src/main/kotlin/ru/ysu/lms/web/admin/AdminController.kt @@ -326,6 +326,7 @@ class AdminController( source = if (va?.manual == true) "вручную" else "авто", submissionId = submissionsByStudent[s.id]?.id, submissionSeq = submissionsByStudent[s.id]?.seq, + submissionFiles = submissionsByStudent[s.id]?.files ?: emptyList(), ) }, ) diff --git a/web/src/main/kotlin/ru/ysu/lms/web/admin/AdminLabView.kt b/web/src/main/kotlin/ru/ysu/lms/web/admin/AdminLabView.kt index 4a57fcd..791e5df 100644 --- a/web/src/main/kotlin/ru/ysu/lms/web/admin/AdminLabView.kt +++ b/web/src/main/kotlin/ru/ysu/lms/web/admin/AdminLabView.kt @@ -17,6 +17,7 @@ data class AdminLabStudent( val source: String, val submissionId: String? = null, val submissionSeq: Int? = null, + val submissionFiles: List = emptyList(), val defenseStatus: String? = null, val defenseCode: String? = null, val defenseSessionId: String? = null, diff --git a/web/src/main/resources/templates/admin/lab.html b/web/src/main/resources/templates/admin/lab.html index 7ab840b..b07d59c 100644 --- a/web/src/main/resources/templates/admin/lab.html +++ b/web/src/main/resources/templates/admin/lab.html @@ -129,6 +129,9 @@ попытка 1 ↓ +
+ main.c +
—