Add landing
Some checks failed
CI / checks (push) Failing after 4m56s

This commit is contained in:
Микаэл Оганесян
2026-04-12 06:17:02 +03:00
parent 84586b5ce2
commit abcd49e117
35 changed files with 2342 additions and 75 deletions

845
docs/doc_v2.json Normal file
View File

@@ -0,0 +1,845 @@
{
"schemes": [],
"swagger": "2.0",
"info": {
"description": "REST API for SparkProctoring — proctoring session management, telemetry, and video streaming.",
"title": "SparkProctoring API",
"contact": {},
"version": "1.0"
},
"host": "sparkguardian.ru",
"basePath": "/api/v1",
"paths": {
"/me": {
"get": {
"security": [
{
"ForwardAuth": []
}
],
"description": "Returns the authenticated user's profile.",
"produces": [
"application/json"
],
"tags": [
"auth"
],
"summary": "Get current user info",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handler.MeResponse"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/handler.ErrorResponse"
}
}
}
}
},
"/sessions": {
"get": {
"security": [
{
"ForwardAuth": []
}
],
"description": "Paginated list of all sessions with summary stats.",
"produces": [
"application/json"
],
"tags": [
"sessions"
],
"summary": "List proctoring sessions",
"parameters": [
{
"type": "integer",
"description": "Max results (default 50, max 100)",
"name": "limit",
"in": "query"
},
{
"type": "integer",
"description": "Offset (default 0)",
"name": "offset",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handler.SessionListResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/handler.ErrorResponse"
}
}
}
},
"post": {
"security": [
{
"ForwardAuth": []
}
],
"description": "Only teachers and admins may create sessions. Returns session_key for agent auth.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"sessions"
],
"summary": "Create a proctoring session",
"parameters": [
{
"description": "Session title",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/handler.CreateSessionRequest"
}
}
],
"responses": {
"201": {
"description": "Created",
"schema": {
"$ref": "#/definitions/handler.CreateSessionResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/handler.ErrorResponse"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/handler.ErrorResponse"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/handler.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/handler.ErrorResponse"
}
}
}
}
},
"/sessions/{id}": {
"get": {
"security": [
{
"ForwardAuth": []
}
],
"description": "Returns session info, stream metadata, and event counts.",
"produces": [
"application/json"
],
"tags": [
"sessions"
],
"summary": "Get session details",
"parameters": [
{
"type": "integer",
"description": "Session ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handler.SessionDetailResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/handler.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/handler.ErrorResponse"
}
}
}
}
},
"/sessions/{id}/events": {
"get": {
"security": [
{
"ForwardAuth": []
}
],
"description": "Downloads encrypted .bin chunks from S3, decrypts, and returns parsed events with optional time range filtering.",
"produces": [
"application/json"
],
"tags": [
"telemetry"
],
"summary": "Get parsed telemetry events",
"parameters": [
{
"type": "integer",
"description": "Session ID",
"name": "id",
"in": "path",
"required": true
},
{
"type": "integer",
"description": "Start timestamp (Unix ms)",
"name": "from",
"in": "query"
},
{
"type": "integer",
"description": "End timestamp (Unix ms)",
"name": "to",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handler.ParsedEventsResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/handler.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/handler.ErrorResponse"
}
}
}
}
},
"/sessions/{id}/fingerprint/full": {
"get": {
"security": [
{
"ForwardAuth": []
}
],
"produces": [
"application/json"
],
"tags": [
"fingerprint"
],
"summary": "Get fingerprint full snapshot",
"parameters": [
{
"type": "integer",
"description": "Session ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK"
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/handler.ErrorResponse"
}
}
}
}
},
"/sessions/{id}/fingerprint/heartbeats": {
"get": {
"security": [
{
"ForwardAuth": []
}
],
"produces": [
"application/json"
],
"tags": [
"fingerprint"
],
"summary": "Get fingerprint heartbeats",
"parameters": [
{
"type": "integer",
"description": "Session ID",
"name": "id",
"in": "path",
"required": true
},
{
"type": "integer",
"description": "Start timestamp (Unix ms)",
"name": "from",
"in": "query"
},
{
"type": "integer",
"description": "End timestamp (Unix ms)",
"name": "to",
"in": "query"
},
{
"type": "integer",
"description": "Max entries to return",
"name": "limit",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/sessions/{id}/fingerprint/summary": {
"get": {
"security": [
{
"ForwardAuth": []
}
],
"produces": [
"application/json"
],
"tags": [
"fingerprint"
],
"summary": "Get fingerprint summary",
"parameters": [
{
"type": "integer",
"description": "Session ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/sessions/{id}/playlist/{stream_type}.m3u8": {
"get": {
"security": [
{
"ForwardAuth": []
}
],
"description": "Returns M3U8 playlist for a session's video stream.",
"produces": [
"application/vnd.apple.mpegurl"
],
"tags": [
"streaming"
],
"summary": "Get HLS playlist",
"parameters": [
{
"type": "integer",
"description": "Session ID",
"name": "id",
"in": "path",
"required": true
},
{
"enum": [
"screen",
"webcam"
],
"type": "string",
"description": "Stream type",
"name": "stream_type",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK"
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/handler.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/handler.ErrorResponse"
}
}
}
}
},
"/sessions/{id}/segment/{stream_type}/{chunk_idx}.ts": {
"get": {
"security": [
{
"ForwardAuth": []
}
],
"description": "Proxies a .ts video segment from S3.",
"produces": [
"video/mp2t"
],
"tags": [
"streaming"
],
"summary": "Get video segment",
"parameters": [
{
"type": "integer",
"description": "Session ID",
"name": "id",
"in": "path",
"required": true
},
{
"enum": [
"screen",
"webcam"
],
"type": "string",
"description": "Stream type",
"name": "stream_type",
"in": "path",
"required": true
},
{
"type": "integer",
"description": "Chunk index",
"name": "chunk_idx",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK"
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/handler.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/handler.ErrorResponse"
}
}
}
}
},
"/sessions/{id}/telemetry": {
"get": {
"security": [
{
"ForwardAuth": []
}
],
"description": "Returns all telemetry events for a session.",
"produces": [
"application/json"
],
"tags": [
"telemetry"
],
"summary": "List telemetry events",
"parameters": [
{
"type": "integer",
"description": "Session ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/handler.TelemetryEvent"
}
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/handler.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/handler.ErrorResponse"
}
}
}
}
},
"/upload": {
"post": {
"security": [
{
"BearerAuth": []
}
],
"description": "Upload .ts (video) or .bin (event) file for a session. Max 64 MB.",
"consumes": [
"multipart/form-data"
],
"tags": [
"agent"
],
"summary": "Upload a video or event chunk",
"parameters": [
{
"type": "integer",
"description": "Session ID",
"name": "session_id",
"in": "formData",
"required": true
},
{
"type": "integer",
"description": "Chunk index",
"name": "chunk_idx",
"in": "formData",
"required": true
},
{
"enum": [
"screen",
"webcam"
],
"type": "string",
"description": "Stream type (default: screen)",
"name": "stream_type",
"in": "formData"
},
{
"type": "file",
"description": "Chunk file (.ts or .bin)",
"name": "file",
"in": "formData",
"required": true
}
],
"responses": {
"200": {
"description": "OK"
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/handler.ErrorResponse"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/handler.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/handler.ErrorResponse"
}
}
}
}
}
},
"definitions": {
"handler.CreateSessionRequest": {
"type": "object",
"properties": {
"title": {
"type": "string",
"example": "Экзамен по ОС"
}
}
},
"handler.CreateSessionResponse": {
"type": "object",
"properties": {
"created_at": {
"type": "string",
"example": "2026-04-05T12:00:00Z"
},
"id": {
"type": "integer",
"example": 1
},
"session_key": {
"type": "string",
"example": "1"
},
"status": {
"type": "string",
"example": "pending"
},
"title": {
"type": "string",
"example": "Экзамен по ОС"
}
}
},
"handler.ErrorResponse": {
"type": "object",
"properties": {
"code": {
"type": "string",
"example": "NOT_FOUND"
},
"error": {
"type": "string",
"example": "session not found"
}
}
},
"handler.MeResponse": {
"type": "object",
"properties": {
"email": {
"type": "string",
"example": "teacher@example.com"
},
"id": {
"type": "integer",
"example": 1
},
"role": {
"type": "string",
"example": "teacher"
}
}
},
"handler.ParsedEvent": {
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"type": "integer"
}
},
"event_type": {
"type": "string",
"example": "keyboard"
},
"timestamp": {
"type": "integer",
"example": 1711360200000
}
}
},
"handler.ParsedEventsResponse": {
"type": "object",
"properties": {
"count": {
"type": "integer",
"example": 42
},
"events": {
"type": "array",
"items": {
"$ref": "#/definitions/handler.ParsedEvent"
}
},
"session_id": {
"type": "integer",
"example": 1
}
}
},
"handler.SessionDetailResponse": {
"type": "object",
"properties": {
"session": {
"$ref": "#/definitions/handler.SessionSummary"
},
"streams": {
"type": "array",
"items": {
"$ref": "#/definitions/handler.StreamInfo"
}
}
}
},
"handler.SessionListResponse": {
"type": "object",
"properties": {
"limit": {
"type": "integer",
"example": 50
},
"offset": {
"type": "integer",
"example": 0
},
"sessions": {
"type": "array",
"items": {
"$ref": "#/definitions/handler.SessionSummary"
}
},
"total": {
"type": "integer",
"example": 5
}
}
},
"handler.SessionSummary": {
"type": "object",
"properties": {
"chunks_total": {
"type": "integer",
"example": 42
},
"ended_at": {
"type": "string"
},
"events_total": {
"type": "integer",
"example": 128
},
"id": {
"type": "string",
"example": "1"
},
"started_at": {
"type": "string",
"example": "2026-04-05T12:00:00Z"
},
"status": {
"type": "string",
"example": "pending"
},
"user_id": {
"type": "string",
"example": "1"
}
}
},
"handler.StreamInfo": {
"type": "object",
"properties": {
"chunk_count": {
"type": "integer",
"example": 10
},
"duration_ms": {
"type": "integer",
"example": 60000
},
"playlist_url": {
"type": "string",
"example": "/api/v1/sessions/1/playlist/screen.m3u8"
},
"stream_type": {
"type": "string",
"example": "screen"
}
}
},
"handler.TelemetryEvent": {
"type": "object",
"properties": {
"created_at": {
"type": "string",
"example": "2026-04-05T12:00:00Z"
},
"event_type": {
"type": "string",
"example": "keyboard"
},
"id": {
"type": "integer",
"example": 1
},
"payload": {
"type": "array",
"items": {
"type": "integer"
}
},
"session_id": {
"type": "integer",
"example": 1
},
"user_id": {
"type": "integer",
"example": 42
}
}
}
},
"securityDefinitions": {
"BearerAuth": {
"description": "JWT token with \"Bearer \" prefix",
"type": "apiKey",
"name": "Authorization",
"in": "header"
},
"ForwardAuth": {
"description": "Authelia Forward Auth — set automatically by Traefik",
"type": "apiKey",
"name": "Remote-User",
"in": "header"
}
}
}