Initial commit from monorepo

This commit is contained in:
Ruslan Bakiev
2026-01-07 09:10:35 +07:00
commit 3db50d9637
371 changed files with 43223 additions and 0 deletions

View File

@@ -0,0 +1,40 @@
import { defineEventHandler, createError } from 'h3'
import type LogtoClient from '@logto/node'
export default defineEventHandler(async (event) => {
const client = event.context.logtoClient as LogtoClient | undefined
const user = event.context.logtoUser
if (!client) {
return {
authenticated: false,
user: null,
idToken: null,
refreshToken: null
}
}
let idToken = null
let refreshToken = null
try {
idToken = await client.getIdToken()
}
catch {
// Ignore
}
try {
refreshToken = await client.getRefreshToken()
}
catch {
// Ignore
}
return {
authenticated: true,
user,
idToken,
refreshToken: refreshToken ? `${refreshToken.substring(0, 20)}...` : null
}
})

View File

@@ -0,0 +1,18 @@
import { defineEventHandler, createError } from 'h3'
import type LogtoClient from '@logto/node'
export default defineEventHandler(async (event) => {
const client = event.context.logtoClient as LogtoClient | undefined
if (!client) {
throw createError({ statusCode: 401, message: 'Not authenticated' })
}
try {
const claims = await client.getIdTokenClaims()
return { claims }
}
catch {
return { claims: null }
}
})

View File

@@ -0,0 +1,18 @@
import { defineEventHandler, createError } from 'h3'
import type LogtoClient from '@logto/node'
export default defineEventHandler(async (event) => {
const client = event.context.logtoClient as LogtoClient | undefined
if (!client) {
throw createError({ statusCode: 401, message: 'Not authenticated' })
}
try {
const idToken = await client.getIdToken()
return { id_token: idToken }
}
catch {
return { id_token: null }
}
})

View File

@@ -0,0 +1,76 @@
import { defineEventHandler, createError } from 'h3'
import type LogtoClient from '@logto/node'
const RESOURCES = {
teams: 'https://teams.optovia.ru',
exchange: 'https://exchange.optovia.ru',
orders: 'https://orders.optovia.ru',
kyc: 'https://kyc.optovia.ru',
billing: 'https://billing.optovia.ru'
} as const
export type ResourceKey = keyof typeof RESOURCES
export interface TokenInfo {
token: string
expiresAt: number
}
export interface RefreshResponse {
tokens: Partial<Record<ResourceKey, TokenInfo>>
}
function decodeTokenExpiry(token: string): number {
try {
const payload = token.split('.')[1]
if (payload) {
const decoded = JSON.parse(Buffer.from(payload, 'base64').toString('utf8'))
if (decoded.exp) {
return decoded.exp * 1000
}
}
}
catch {
// ignore
}
return Date.now() + 3600 * 1000 // default 1 hour
}
/**
* Refresh all access tokens for all resources.
* Gets organizationId from logtoUser (first organization).
* Returns tokens with expiry timestamps.
*/
export default defineEventHandler(async (event): Promise<RefreshResponse> => {
const client = event.context.logtoClient as LogtoClient | undefined
const logtoUser = event.context.logtoUser as { organizations?: string[] } | undefined
if (!client) {
throw createError({ statusCode: 401, message: 'Not authenticated' })
}
// Get first organization from Logto user
const organizationId = logtoUser?.organizations?.[0]
const tokens: Partial<Record<ResourceKey, TokenInfo>> = {}
// Fetch all tokens in parallel with organization context
await Promise.all(
(Object.entries(RESOURCES) as [ResourceKey, string][]).map(async ([key, resource]) => {
try {
const token = await client.getAccessToken(resource, organizationId)
if (token) {
tokens[key] = {
token,
expiresAt: decodeTokenExpiry(token)
}
}
}
catch {
// Token not available for this resource, skip
}
})
)
return { tokens }
})

148
server/api/companies.get.ts Normal file
View File

@@ -0,0 +1,148 @@
export default defineEventHandler(async (event) => {
const query = getQuery(event)
const locale = query.locale || 'ru'
// Service companies data with translations
const companiesData = {
ru: {
logistics: [
{
id: 1,
name: "RosLogistic",
price: 15000,
description: "Federal transportation network",
rating: 4.8,
reviews: 156,
experience: "15 years experience",
verified: true
},
{
id: 2,
name: "TransService",
price: 12000,
description: "Regional carrier",
rating: 4.5,
reviews: 89,
experience: "8 years experience",
verified: false
}
],
banks: [
{
id: 1,
name: "VTB",
rate: 12.5,
terms: "Raw materials purchase loan",
period: "up to 24 months",
rating: 4.7,
reviews: 89,
verified: true
},
{
id: 2,
name: "Sberbank",
rate: 13.2,
terms: "Trade financing",
period: "up to 18 months",
rating: 4.9,
reviews: 145,
verified: true
}
],
insurance: [
{
id: 1,
name: "RESO",
cost: 3500,
coverage: "Cargo insurance up to 500M₽",
rating: 4.6,
reviews: 67,
verified: true
}
],
laboratories: [
{
id: 1,
name: "MetalTest",
price: 8000,
tests: "Chemical analysis, mechanical properties",
certification: "ISO 9001",
rating: 4.7,
reviews: 45,
verified: true
}
]
},
en: {
logistics: [
{
id: 1,
name: "RusLogistics",
price: 15000,
description: "Federal transportation network",
rating: 4.8,
reviews: 156,
experience: "15 years experience",
verified: true
},
{
id: 2,
name: "TransService",
price: 12000,
description: "Regional carrier",
rating: 4.5,
reviews: 89,
experience: "8 years experience",
verified: false
}
],
banks: [
{
id: 1,
name: "VTB Bank",
rate: 12.5,
terms: "Raw materials purchase loan",
period: "up to 24 months",
rating: 4.7,
reviews: 89,
verified: true
},
{
id: 2,
name: "Sberbank",
rate: 13.2,
terms: "Trade financing",
period: "up to 18 months",
rating: 4.9,
reviews: 145,
verified: true
}
],
insurance: [
{
id: 1,
name: "RESO Insurance",
cost: 3500,
coverage: "Cargo insurance up to 500M₽",
rating: 4.6,
reviews: 67,
verified: true
}
],
laboratories: [
{
id: 1,
name: "MetalTest Lab",
price: 8000,
tests: "Chemical analysis, mechanical properties",
certification: "ISO 9001",
rating: 4.7,
reviews: 45,
verified: true
}
]
}
}
return companiesData[locale as 'ru' | 'en'] || companiesData.ru
})

View File

@@ -0,0 +1,34 @@
export default defineEventHandler(async (event) => {
const query = getQuery(event)
const locale = query.locale || 'ru'
// Cities data with translations
const locationsData = {
ru: [
{ name: 'Moscow', region: 'Central Federal District', distance: '0 km', coords: [55.76, 37.64] },
{ name: 'St. Petersburg', region: 'Northwestern Federal District', distance: '635 km', coords: [59.93, 30.34] },
{ name: 'Novosibirsk', region: 'Siberian Federal District', distance: '3354 km', coords: [55.03, 82.92] },
{ name: 'Yekaterinburg', region: 'Ural Federal District', distance: '1416 km', coords: [56.84, 60.65] },
{ name: 'Kazan', region: 'Volga Federal District', distance: '719 km', coords: [55.83, 49.09] },
{ name: 'Nizhny Novgorod', region: 'Volga Federal District', distance: '411 km', coords: [56.33, 44.00] },
{ name: 'Chelyabinsk', region: 'Ural Federal District', distance: '1777 km', coords: [55.16, 61.43] },
{ name: 'Samara', region: 'Volga Federal District', distance: '862 km', coords: [53.20, 50.15] },
{ name: 'Omsk', region: 'Siberian Federal District', distance: '2555 km', coords: [54.97, 73.37] },
{ name: 'Rostov-on-Don', region: 'Southern Federal District', distance: '925 km', coords: [47.23, 39.72] }
],
en: [
{ name: 'Moscow', region: 'Central Federal District', distance: '0 km', coords: [55.76, 37.64] },
{ name: 'St. Petersburg', region: 'Northwestern Federal District', distance: '635 km', coords: [59.93, 30.34] },
{ name: 'Novosibirsk', region: 'Siberian Federal District', distance: '3354 km', coords: [55.03, 82.92] },
{ name: 'Yekaterinburg', region: 'Ural Federal District', distance: '1416 km', coords: [56.84, 60.65] },
{ name: 'Kazan', region: 'Volga Federal District', distance: '719 km', coords: [55.83, 49.09] },
{ name: 'Nizhny Novgorod', region: 'Volga Federal District', distance: '411 km', coords: [56.33, 44.00] },
{ name: 'Chelyabinsk', region: 'Ural Federal District', distance: '1777 km', coords: [55.16, 61.43] },
{ name: 'Samara', region: 'Volga Federal District', distance: '862 km', coords: [53.20, 50.15] },
{ name: 'Omsk', region: 'Siberian Federal District', distance: '2555 km', coords: [54.97, 73.37] },
{ name: 'Rostov-on-Don', region: 'Southern Federal District', distance: '925 km', coords: [47.23, 39.72] }
]
}
return locationsData[locale as 'ru' | 'en'] || locationsData.ru
})

126
server/api/suppliers.get.ts Normal file
View File

@@ -0,0 +1,126 @@
export default defineEventHandler(async (event) => {
const query = getQuery(event)
const locale = query.locale || 'ru'
// Suppliers data with translations
const suppliersData = {
ru: [
{
id: 1,
name: "UralMetal",
location: "Yekaterinburg",
coords: [56.84, 60.65],
totalPrice: 180000,
rating: 4.8,
reviews: 127,
experience: "15 years in business",
verified: true,
logistics: [
{
type: 'auto',
route: 'Auto delivery',
time: '5-7 days',
cost: 15000
},
{
type: 'multimodal',
route: 'Auto + Sea + Auto',
time: '12-15 days',
cost: 8000
},
{
type: 'rail',
route: 'Auto + Rail + Auto',
time: '8-10 days',
cost: 12000
}
]
},
{
id: 2,
name: "SiberiaSteel",
location: "Novosibirsk",
coords: [55.03, 82.92],
totalPrice: 175000,
rating: 4.6,
reviews: 89,
experience: "12 years in business",
verified: true,
logistics: [
{
type: 'auto',
route: 'Auto delivery',
time: '6-8 days',
cost: 18000
},
{
type: 'rail',
route: 'Auto + Rail + Auto',
time: '10-12 days',
cost: 10000
}
]
}
],
en: [
{
id: 1,
name: "UralMetal",
location: "Yekaterinburg",
coords: [56.84, 60.65],
totalPrice: 180000,
rating: 4.8,
reviews: 127,
experience: "15 years in business",
verified: true,
logistics: [
{
type: 'auto',
route: 'Auto delivery',
time: '5-7 days',
cost: 15000
},
{
type: 'multimodal',
route: 'Auto + Sea + Auto',
time: '12-15 days',
cost: 8000
},
{
type: 'rail',
route: 'Auto + Rail + Auto',
time: '8-10 days',
cost: 12000
}
]
},
{
id: 2,
name: "SiberiaSteel",
location: "Novosibirsk",
coords: [55.03, 82.92],
totalPrice: 175000,
rating: 4.6,
reviews: 89,
experience: "12 years in business",
verified: true,
logistics: [
{
type: 'auto',
route: 'Auto delivery',
time: '6-8 days',
cost: 18000
},
{
type: 'rail',
route: 'Auto + Rail + Auto',
time: '10-12 days',
cost: 10000
}
]
}
]
}
return suppliersData[locale as 'ru' | 'en'] || suppliersData.ru
})