Submissions: reject XML project files, show submitted file names to admin
This commit is contained in:
@@ -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("<?xml") ||
|
||||
head.startsWith("<solution") ||
|
||||
head.startsWith("<project") ||
|
||||
head.startsWith("<configurations")
|
||||
}
|
||||
|
||||
private fun filesOf(s: Submission): List<String> =
|
||||
storage.listFiles(storage.dirFor(s.studentId, s.labId, s.seq))
|
||||
|
||||
|
||||
@@ -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(),
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
@@ -17,6 +17,7 @@ data class AdminLabStudent(
|
||||
val source: String,
|
||||
val submissionId: String? = null,
|
||||
val submissionSeq: Int? = null,
|
||||
val submissionFiles: List<String> = emptyList(),
|
||||
val defenseStatus: String? = null,
|
||||
val defenseCode: String? = null,
|
||||
val defenseSessionId: String? = null,
|
||||
|
||||
@@ -129,6 +129,9 @@
|
||||
<a th:if="${s.submissionId}" class="cell-link"
|
||||
th:href="'/admin/submissions/' + ${s.submissionId} + '/download'">
|
||||
попытка <span th:text="${s.submissionSeq}">1</span> ↓</a>
|
||||
<div th:if="${!#lists.isEmpty(s.submissionFiles)}" class="text-muted text-sm font-mono">
|
||||
<span th:text="${#strings.listJoin(s.submissionFiles, ', ')}">main.c</span>
|
||||
</div>
|
||||
<span th:unless="${s.submissionId}" class="text-muted">—</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
Reference in New Issue
Block a user