From 6f5012cb6c5354bf2275920ae944f758898f957d Mon Sep 17 00:00:00 2001 From: wilson Date: Wed, 25 Mar 2026 21:09:38 +0000 Subject: [PATCH] add login functionality --- frontend/script/download-openapi.sh | 2 + frontend/src/app.d.ts | 7 +- frontend/src/hooks.server.ts | 13 + frontend/src/lib/apiClient.ts | 8 +- frontend/src/lib/openapi.json | 883 +--------------------- frontend/src/routes/login/+page.server.ts | 39 + frontend/src/routes/login/+page.svelte | 34 + frontend/src/routes/login/+server.ts | 5 - 8 files changed, 99 insertions(+), 892 deletions(-) create mode 100644 frontend/script/download-openapi.sh create mode 100644 frontend/src/hooks.server.ts create mode 100644 frontend/src/routes/login/+page.server.ts delete mode 100644 frontend/src/routes/login/+server.ts diff --git a/frontend/script/download-openapi.sh b/frontend/script/download-openapi.sh new file mode 100644 index 0000000..00750a5 --- /dev/null +++ b/frontend/script/download-openapi.sh @@ -0,0 +1,2 @@ +# Makes a request to localhost:8000/openapi.json and saves the result in ./src/lib/openapi.json +curl -o ./src/lib/openapi.json http://localhost:8000/openapi.json diff --git a/frontend/src/app.d.ts b/frontend/src/app.d.ts index da08e6d..5a95820 100644 --- a/frontend/src/app.d.ts +++ b/frontend/src/app.d.ts @@ -1,9 +1,14 @@ +import type { ApiClient } from './client/types.gen.ts'; + // See https://svelte.dev/docs/kit/types#app.d.ts // for information about these interfaces declare global { namespace App { // interface Error {} - // interface Locals {} + interface Locals { + apiClient?: ApiClient; + authToken: string | null; + } // interface PageData {} // interface PageState {} // interface Platform {} diff --git a/frontend/src/hooks.server.ts b/frontend/src/hooks.server.ts new file mode 100644 index 0000000..94547f2 --- /dev/null +++ b/frontend/src/hooks.server.ts @@ -0,0 +1,13 @@ +import type { Handle } from '@sveltejs/kit'; +import { client } from './lib/apiClient.ts'; + +export const handle: Handle = async ({ event, resolve }) => { + event.locals.apiClient = client; + + const authToken = event.cookies.get('auth_token'); + event.locals.authToken = authToken || null; + + const response = resolve(event); + + return response; +}; diff --git a/frontend/src/lib/apiClient.ts b/frontend/src/lib/apiClient.ts index f966162..c070873 100644 --- a/frontend/src/lib/apiClient.ts +++ b/frontend/src/lib/apiClient.ts @@ -1,4 +1,4 @@ -import { PUBLIC_API_BASE_URL } from '$env/static/public' -import { createClient } from '../client/client' - -export const apiClient = createClient({ baseUrl: PUBLIC_API_BASE_URL }) +import { PUBLIC_API_BASE_URL } from '$env/static/public'; +import { client } from '../client/client.gen.ts'; +client.setConfig({ baseUrl: PUBLIC_API_BASE_URL }); +export { client }; diff --git a/frontend/src/lib/openapi.json b/frontend/src/lib/openapi.json index 8d4762c..f38a2bf 100644 --- a/frontend/src/lib/openapi.json +++ b/frontend/src/lib/openapi.json @@ -1,882 +1 @@ -{ -"openapi": "3.1.0", -"info": { -"title": "Language Learning API", -"version": "0.1.0" -}, -"paths": { -"/api/pos/": { -"post": { -"tags": [ -"api", -"api", -"pos" -], -"summary": "Analyze Pos", -"operationId": "analyze_pos_api_pos__post", -"requestBody": { -"content": { -"application/json": { -"schema": { -"$ref": "#/components/schemas/POSRequest" -} -} -}, -"required": true -}, -"responses": { -"200": { -"description": "Successful Response", -"content": { -"application/json": { -"schema": { -"$ref": "#/components/schemas/POSResponse" -} -} -} -}, -"422": { -"description": "Validation Error", -"content": { -"application/json": { -"schema": { -"$ref": "#/components/schemas/HTTPValidationError" -} -} -} -} -}, -"security": [ -{ -"HTTPBearer": [] -} -] -} -}, -"/api/translate": { -"get": { -"tags": [ -"api", -"api", -"translate" -], -"summary": "Translate text to a target language", -"operationId": "translate_text_api_translate_get", -"security": [ -{ -"HTTPBearer": [] -} -], -"parameters": [ -{ -"name": "text", -"in": "query", -"required": true, -"schema": { -"type": "string", -"title": "Text" -} -}, -{ -"name": "target_language", -"in": "query", -"required": true, -"schema": { -"type": "string", -"title": "Target Language" -} -}, -{ -"name": "context", -"in": "query", -"required": false, -"schema": { -"anyOf": [ -{ -"type": "string" -}, -{ -"type": "null" -} -], -"title": "Context" -} -} -], -"responses": { -"200": { -"description": "Successful Response", -"content": { -"application/json": { -"schema": { -"$ref": "#/components/schemas/TranslationResponse" -} -} -} -}, -"422": { -"description": "Validation Error", -"content": { -"application/json": { -"schema": { -"$ref": "#/components/schemas/HTTPValidationError" -} -} -} -} -} -} -}, -"/api/generate": { -"post": { -"tags": [ -"api", -"api" -], -"summary": "Create Generation Job", -"operationId": "create_generation_job_api_generate_post", -"requestBody": { -"content": { -"application/json": { -"schema": { -"$ref": "#/components/schemas/GenerationRequest" -} -} -}, -"required": true -}, -"responses": { -"202": { -"description": "Successful Response", -"content": { -"application/json": { -"schema": { -"$ref": "#/components/schemas/GenerationResponse" -} -} -} -}, -"422": { -"description": "Validation Error", -"content": { -"application/json": { -"schema": { -"$ref": "#/components/schemas/HTTPValidationError" -} -} -} -} -}, -"security": [ -{ -"HTTPBearer": [] -} -] -} -}, -"/api/jobs/": { -"get": { -"tags": [ -"api", -"jobs" -], -"summary": "Get Jobs", -"operationId": "get_jobs_api_jobs__get", -"responses": { -"200": { -"description": "Successful Response", -"content": { -"application/json": { -"schema": { -"$ref": "#/components/schemas/JobListResponse" -} -} -} -} -}, -"security": [ -{ -"HTTPBearer": [] -} -] -} -}, -"/api/jobs/{job_id}": { -"get": { -"tags": [ -"api", -"jobs" -], -"summary": "Get Job", -"operationId": "get_job_api_jobs__job_id__get", -"security": [ -{ -"HTTPBearer": [] -} -], -"parameters": [ -{ -"name": "job_id", -"in": "path", -"required": true, -"schema": { -"type": "string", -"title": "Job Id" -} -} -], -"responses": { -"200": { -"description": "Successful Response", -"content": { -"application/json": { -"schema": { -"$ref": "#/components/schemas/JobResponse" -} -} -} -}, -"422": { -"description": "Validation Error", -"content": { -"application/json": { -"schema": { -"$ref": "#/components/schemas/HTTPValidationError" -} -} -} -} -} -} -}, -"/api/jobs/{job_id}/regenerate-audio": { -"post": { -"tags": [ -"api", -"jobs" -], -"summary": "Regenerate Audio", -"operationId": "regenerate_audio_api_jobs__job_id__regenerate_audio_post", -"security": [ -{ -"HTTPBearer": [] -} -], -"parameters": [ -{ -"name": "job_id", -"in": "path", -"required": true, -"schema": { -"type": "string", -"title": "Job Id" -} -} -], -"responses": { -"202": { -"description": "Successful Response", -"content": { -"application/json": { -"schema": { -"type": "object", -"additionalProperties": true, -"title": "Response Regenerate Audio Api Jobs Job Id Regenerate Audio Post" -} -} -} -}, -"422": { -"description": "Validation Error", -"content": { -"application/json": { -"schema": { -"$ref": "#/components/schemas/HTTPValidationError" -} -} -} -} -} -} -}, -"/auth/register": { -"post": { -"tags": [ -"auth" -], -"summary": "Register", -"operationId": "register_auth_register_post", -"requestBody": { -"content": { -"application/json": { -"schema": { -"$ref": "#/components/schemas/RegisterRequest" -} -} -}, -"required": true -}, -"responses": { -"201": { -"description": "Successful Response", -"content": { -"application/json": { -"schema": {} -} -} -}, -"422": { -"description": "Validation Error", -"content": { -"application/json": { -"schema": { -"$ref": "#/components/schemas/HTTPValidationError" -} -} -} -} -} -} -}, -"/auth/login": { -"post": { -"tags": [ -"auth" -], -"summary": "Login", -"operationId": "login_auth_login_post", -"requestBody": { -"content": { -"application/json": { -"schema": { -"$ref": "#/components/schemas/LoginRequest" -} -} -}, -"required": true -}, -"responses": { -"200": { -"description": "Successful Response", -"content": { -"application/json": { -"schema": { -"$ref": "#/components/schemas/TokenResponse" -} -} -} -}, -"422": { -"description": "Validation Error", -"content": { -"application/json": { -"schema": { -"$ref": "#/components/schemas/HTTPValidationError" -} -} -} -} -} -} -}, -"/media/{filename}": { -"get": { -"tags": [ -"media" -], -"summary": "Get Media File", -"operationId": "get_media_file_media__filename__get", -"security": [ -{ -"HTTPBearer": [] -} -], -"parameters": [ -{ -"name": "filename", -"in": "path", -"required": true, -"schema": { -"type": "string", -"title": "Filename" -} -} -], -"responses": { -"200": { -"description": "Successful Response", -"content": { -"application/json": { -"schema": {} -} -} -}, -"422": { -"description": "Validation Error", -"content": { -"application/json": { -"schema": { -"$ref": "#/components/schemas/HTTPValidationError" -} -} -} -} -} -} -}, -"/health": { -"get": { -"summary": "Health", -"operationId": "health_health_get", -"responses": { -"200": { -"description": "Successful Response", -"content": { -"application/json": { -"schema": { -"additionalProperties": true, -"type": "object", -"title": "Response Health Health Get" -} -} -} -} -} -} -} -}, -"components": { -"schemas": { -"GenerationRequest": { -"properties": { -"target_language": { -"type": "string", -"title": "Target Language" -}, -"complexity_level": { -"type": "string", -"title": "Complexity Level" -}, -"input_texts": { -"items": { -"type": "string" -}, -"type": "array", -"title": "Input Texts" -}, -"topic": { -"anyOf": [ -{ -"type": "string" -}, -{ -"type": "null" -} -], -"title": "Topic" -}, -"source_language": { -"type": "string", -"title": "Source Language", -"default": "en" -} -}, -"type": "object", -"required": [ -"target_language", -"complexity_level", -"input_texts" -], -"title": "GenerationRequest" -}, -"GenerationResponse": { -"properties": { -"job_id": { -"type": "string", -"title": "Job Id" -} -}, -"type": "object", -"required": [ -"job_id" -], -"title": "GenerationResponse" -}, -"HTTPValidationError": { -"properties": { -"detail": { -"items": { -"$ref": "#/components/schemas/ValidationError" -}, -"type": "array", -"title": "Detail" -} -}, -"type": "object", -"title": "HTTPValidationError" -}, -"JobListResponse": { -"properties": { -"jobs": { -"items": { -"$ref": "#/components/schemas/JobSummary" -}, -"type": "array", -"title": "Jobs" -} -}, -"type": "object", -"required": [ -"jobs" -], -"title": "JobListResponse" -}, -"JobResponse": { -"properties": { -"id": { -"type": "string", -"format": "uuid", -"title": "Id" -}, -"status": { -"type": "string", -"title": "Status" -}, -"source_language": { -"type": "string", -"title": "Source Language" -}, -"target_language": { -"type": "string", -"title": "Target Language" -}, -"complexity_level": { -"type": "string", -"title": "Complexity Level" -}, -"created_at": { -"type": "string", -"format": "date-time", -"title": "Created At" -}, -"started_at": { -"anyOf": [ -{ -"type": "string", -"format": "date-time" -}, -{ -"type": "null" -} -], -"title": "Started At" -}, -"completed_at": { -"anyOf": [ -{ -"type": "string", -"format": "date-time" -}, -{ -"type": "null" -} -], -"title": "Completed At" -}, -"generated_text": { -"anyOf": [ -{ -"type": "string" -}, -{ -"type": "null" -} -], -"title": "Generated Text" -}, -"translated_text": { -"anyOf": [ -{ -"type": "string" -}, -{ -"type": "null" -} -], -"title": "Translated Text" -}, -"input_summary": { -"anyOf": [ -{ -"type": "string" -}, -{ -"type": "null" -} -], -"title": "Input Summary" -}, -"audio_url": { -"anyOf": [ -{ -"type": "string" -}, -{ -"type": "null" -} -], -"title": "Audio Url" -}, -"error_message": { -"anyOf": [ -{ -"type": "string" -}, -{ -"type": "null" -} -], -"title": "Error Message" -} -}, -"type": "object", -"required": [ -"id", -"status", -"source_language", -"target_language", -"complexity_level", -"created_at" -], -"title": "JobResponse" -}, -"JobSummary": { -"properties": { -"id": { -"type": "string", -"format": "uuid", -"title": "Id" -}, -"status": { -"type": "string", -"title": "Status" -}, -"created_at": { -"type": "string", -"format": "date-time", -"title": "Created At" -} -}, -"type": "object", -"required": [ -"id", -"status", -"created_at" -], -"title": "JobSummary" -}, -"LoginRequest": { -"properties": { -"email": { -"type": "string", -"format": "email", -"title": "Email" -}, -"password": { -"type": "string", -"title": "Password" -} -}, -"type": "object", -"required": [ -"email", -"password" -], -"title": "LoginRequest" -}, -"POSRequest": { -"properties": { -"text": { -"type": "string", -"title": "Text" -}, -"language": { -"type": "string", -"title": "Language" -} -}, -"type": "object", -"required": [ -"text", -"language" -], -"title": "POSRequest" -}, -"POSResponse": { -"properties": { -"language": { -"type": "string", -"title": "Language" -}, -"tokens": { -"items": { -"$ref": "#/components/schemas/TokenInfo" -}, -"type": "array", -"title": "Tokens" -} -}, -"type": "object", -"required": [ -"language", -"tokens" -], -"title": "POSResponse" -}, -"RegisterRequest": { -"properties": { -"email": { -"type": "string", -"format": "email", -"title": "Email" -}, -"password": { -"type": "string", -"title": "Password" -} -}, -"type": "object", -"required": [ -"email", -"password" -], -"title": "RegisterRequest" -}, -"TokenInfo": { -"properties": { -"text": { -"type": "string", -"title": "Text" -}, -"lemma": { -"type": "string", -"title": "Lemma" -}, -"pos": { -"type": "string", -"title": "Pos" -}, -"tag": { -"type": "string", -"title": "Tag" -}, -"dep": { -"type": "string", -"title": "Dep" -}, -"is_stop": { -"type": "boolean", -"title": "Is Stop" -} -}, -"type": "object", -"required": [ -"text", -"lemma", -"pos", -"tag", -"dep", -"is_stop" -], -"title": "TokenInfo" -}, -"TokenResponse": { -"properties": { -"access_token": { -"type": "string", -"title": "Access Token" -}, -"token_type": { -"type": "string", -"title": "Token Type", -"default": "bearer" -} -}, -"type": "object", -"required": [ -"access_token" -], -"title": "TokenResponse" -}, -"TranslationResponse": { -"properties": { -"text": { -"type": "string", -"title": "Text" -}, -"target_language": { -"type": "string", -"title": "Target Language" -}, -"translated_text": { -"type": "string", -"title": "Translated Text" -} -}, -"type": "object", -"required": [ -"text", -"target_language", -"translated_text" -], -"title": "TranslationResponse" -}, -"ValidationError": { -"properties": { -"loc": { -"items": { -"anyOf": [ -{ -"type": "string" -}, -{ -"type": "integer" -} -] -}, -"type": "array", -"title": "Location" -}, -"msg": { -"type": "string", -"title": "Message" -}, -"type": { -"type": "string", -"title": "Error Type" -}, -"input": { -"title": "Input" -}, -"ctx": { -"type": "object", -"title": "Context" -} -}, -"type": "object", -"required": [ -"loc", -"msg", -"type" -], -"title": "ValidationError" -} -}, -"securitySchemes": { -"HTTPBearer": { -"type": "http", -"scheme": "bearer" -} -} -} -} +{"openapi":"3.1.0","info":{"title":"Language Learning API","version":"0.1.0"},"paths":{"/api/pos/":{"post":{"tags":["api","api","pos"],"summary":"Analyze Pos","operationId":"analyze_pos_api_pos__post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/POSRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/POSResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/translate":{"get":{"tags":["api","api","translate"],"summary":"Translate text to a target language","operationId":"translate_text_api_translate_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"text","in":"query","required":true,"schema":{"type":"string","title":"Text"}},{"name":"target_language","in":"query","required":true,"schema":{"type":"string","title":"Target Language"}},{"name":"context","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Context"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TranslationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/generate":{"post":{"tags":["api","api"],"summary":"Create Generation Job","operationId":"create_generation_job_api_generate_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationRequest"}}},"required":true},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/jobs/":{"get":{"tags":["api"],"summary":"Get Jobs","operationId":"get_jobs_api_jobs__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobListResponse"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/jobs/{job_id}":{"get":{"tags":["api"],"summary":"Get Job","operationId":"get_job_api_jobs__job_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/jobs/{job_id}/regenerate-audio":{"post":{"tags":["api"],"summary":"Regenerate Audio","operationId":"regenerate_audio_api_jobs__job_id__regenerate_audio_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}}],"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Regenerate Audio Api Jobs Job Id Regenerate Audio Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/register":{"post":{"tags":["auth"],"summary":"Register","operationId":"register_auth_register_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/login":{"post":{"tags":["auth"],"summary":"Login","operationId":"login_auth_login_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/media/{filename}":{"get":{"tags":["media"],"summary":"Get Media File","operationId":"get_media_file_media__filename__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"filename","in":"path","required":true,"schema":{"type":"string","title":"Filename"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/health":{"get":{"summary":"Health","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Health Health Get"}}}}}}}},"components":{"schemas":{"GenerationRequest":{"properties":{"target_language":{"type":"string","title":"Target Language"},"complexity_level":{"type":"string","title":"Complexity Level"},"input_texts":{"items":{"type":"string"},"type":"array","title":"Input Texts"},"topic":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Topic"},"source_language":{"type":"string","title":"Source Language","default":"en"}},"type":"object","required":["target_language","complexity_level","input_texts"],"title":"GenerationRequest"},"GenerationResponse":{"properties":{"job_id":{"type":"string","title":"Job Id"}},"type":"object","required":["job_id"],"title":"GenerationResponse"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"JobListResponse":{"properties":{"jobs":{"items":{"$ref":"#/components/schemas/JobSummary"},"type":"array","title":"Jobs"}},"type":"object","required":["jobs"],"title":"JobListResponse"},"JobResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"status":{"type":"string","title":"Status"},"source_language":{"type":"string","title":"Source Language"},"target_language":{"type":"string","title":"Target Language"},"complexity_level":{"type":"string","title":"Complexity Level"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Started At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"},"generated_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Generated Text"},"translated_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Translated Text"},"input_summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Input Summary"},"audio_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Audio Url"},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message"}},"type":"object","required":["id","status","source_language","target_language","complexity_level","created_at"],"title":"JobResponse"},"JobSummary":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"status":{"type":"string","title":"Status"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","status","created_at"],"title":"JobSummary"},"LoginRequest":{"properties":{"email":{"type":"string","format":"email","title":"Email"},"password":{"type":"string","title":"Password"}},"type":"object","required":["email","password"],"title":"LoginRequest"},"POSRequest":{"properties":{"text":{"type":"string","title":"Text"},"language":{"type":"string","title":"Language"}},"type":"object","required":["text","language"],"title":"POSRequest"},"POSResponse":{"properties":{"language":{"type":"string","title":"Language"},"tokens":{"items":{"$ref":"#/components/schemas/TokenInfo"},"type":"array","title":"Tokens"}},"type":"object","required":["language","tokens"],"title":"POSResponse"},"RegisterRequest":{"properties":{"email":{"type":"string","format":"email","title":"Email"},"password":{"type":"string","title":"Password"}},"type":"object","required":["email","password"],"title":"RegisterRequest"},"TokenInfo":{"properties":{"text":{"type":"string","title":"Text"},"lemma":{"type":"string","title":"Lemma"},"pos":{"type":"string","title":"Pos"},"tag":{"type":"string","title":"Tag"},"dep":{"type":"string","title":"Dep"},"is_stop":{"type":"boolean","title":"Is Stop"}},"type":"object","required":["text","lemma","pos","tag","dep","is_stop"],"title":"TokenInfo"},"TokenResponse":{"properties":{"access_token":{"type":"string","title":"Access Token"},"token_type":{"type":"string","title":"Token Type","default":"bearer"}},"type":"object","required":["access_token"],"title":"TokenResponse"},"TranslationResponse":{"properties":{"text":{"type":"string","title":"Text"},"target_language":{"type":"string","title":"Target Language"},"translated_text":{"type":"string","title":"Translated Text"}},"type":"object","required":["text","target_language","translated_text"],"title":"TranslationResponse"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}},"securitySchemes":{"HTTPBearer":{"type":"http","scheme":"bearer"}}}} \ No newline at end of file diff --git a/frontend/src/routes/login/+page.server.ts b/frontend/src/routes/login/+page.server.ts new file mode 100644 index 0000000..c2d3dc5 --- /dev/null +++ b/frontend/src/routes/login/+page.server.ts @@ -0,0 +1,39 @@ +import { loginAuthLoginPost } from '../../client/sdk.gen.ts'; +import { redirect, type Actions, type ServerLoad } from '@sveltejs/kit'; + +export const load: ServerLoad = async ({ locals }) => { + if (locals.authToken) { + return redirect(307, '/'); + } + + return { + isLoggedIn: !!locals.authToken + }; +}; + +export const actions = { + default: async ({ request, locals, cookies }) => { + const formData = await request.formData(); + const email = formData.get('email') as string; + const password = formData.get('password') as string; + + const { response, data } = await loginAuthLoginPost({ + headers: { + 'Content-Type': 'application/json', + Authorization: locals.authToken ? `Bearer ${locals.authToken}` : '' + }, + body: { email, password } + }); + + if (response.status === 200 && data) { + cookies.set('auth_token', data.access_token, { + path: '/', + expires: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000) // 7 days + }); + + return redirect(307, '/'); + } + + return { success: response.status === 200, error: response.status !== 200 ? data : null }; + } +} satisfies Actions; diff --git a/frontend/src/routes/login/+page.svelte b/frontend/src/routes/login/+page.svelte index e69de29..f828e54 100644 --- a/frontend/src/routes/login/+page.svelte +++ b/frontend/src/routes/login/+page.svelte @@ -0,0 +1,34 @@ + + +{#if data.isLoggedIn} +

You are logged in

+{/if} +
+
+ + +
+
+ + +
+ +
diff --git a/frontend/src/routes/login/+server.ts b/frontend/src/routes/login/+server.ts deleted file mode 100644 index 7350b29..0000000 --- a/frontend/src/routes/login/+server.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { createClient } from '../../client/client' -import { loginAuthLoginPost } from '../../client/sdk.gen.ts' - -const client = createClient({ baseUrl: 'http://localhost:8000' }) -loginAuthLoginPost({ client, body: { email: 'test', password: 'test' } })