fet: Add changes lol
Signed-off-by: Florian BOUILLON <f.bouillon@aptatio.com>
This commit is contained in:
2
src/env.d.ts
vendored
2
src/env.d.ts
vendored
@ -22,7 +22,7 @@ declare namespace App {
|
||||
authKey?: string
|
||||
responseBuilder: {
|
||||
body(body: string | Buffer | object | null | undefined): this
|
||||
headers(headers: HeadersInit ): this
|
||||
headers(headers: Record<string, string>): this
|
||||
addHeader(key: string, value: string): this
|
||||
addHeaders(headers: Record<string, string>): this
|
||||
removeHeader(key: string): this
|
||||
|
@ -1,4 +1,5 @@
|
||||
---
|
||||
import { WifiOff } from 'lucide-astro'
|
||||
export interface Props {
|
||||
title: string;
|
||||
}
|
||||
@ -16,21 +17,21 @@ const { title } = Astro.props;
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
<title>{title}</title>
|
||||
</head>
|
||||
<body>
|
||||
<slot />
|
||||
<body class="bg-gray-50">
|
||||
|
||||
<aside class="fixed top-0 left-0 z-40 w-64 p-4 h-screen bg-white border-r-2 border-gray-100">
|
||||
<div class="mb-2 flex">
|
||||
<img src="/logo.svg" class="w-1/2" alt="FI3D Logo">
|
||||
<WifiOff class='pokemon' />
|
||||
</div>
|
||||
<ul class="space-y-2 font-medium">
|
||||
<li class="p-4 w-full bg-red-100 hover:bg-red-200 cursor-pointer text-center">Item</li>
|
||||
</ul>
|
||||
</aside>
|
||||
|
||||
<div class="p-4 sm:ml-64">
|
||||
<slot />
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
<style is:global>
|
||||
:root {
|
||||
--accent: 124, 58, 237;
|
||||
--accent-gradient: linear-gradient(45deg, rgb(var(--accent)), #da62c4 30%, white 60%);
|
||||
}
|
||||
html {
|
||||
font-family: system-ui, sans-serif;
|
||||
background-color: #F6F6F6;
|
||||
}
|
||||
code {
|
||||
font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
|
||||
Bitstream Vera Sans Mono, Courier New, monospace;
|
||||
}
|
||||
</style>
|
||||
|
3
src/libs/README.md
Normal file
3
src/libs/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Libs
|
||||
|
||||
Globally independent objects/classes/functions that MUST be unit testable by themselve
|
@ -1,6 +1,6 @@
|
||||
import { objectLoop } from '@dzeio/object-util'
|
||||
import StatusCode from './HTTP/StatusCode'
|
||||
import { buildRFC7807 } from './RFCs/RFC7807'
|
||||
import StatusCode from './StatusCode'
|
||||
import ResponseBuilder from './ResponseBuilder'
|
||||
|
||||
interface StorageItem {
|
||||
pointsRemaining: number
|
||||
@ -33,36 +33,40 @@ export default class RateLimiter {
|
||||
|
||||
private storage: Record<string, StorageItem> = {}
|
||||
|
||||
public constructor(
|
||||
private points = RateLimiter.points,
|
||||
private timeSpan = RateLimiter.timeSpan
|
||||
) {}
|
||||
|
||||
public consume(key: string, value: number = 1): Response | RateLimitHeaders {
|
||||
let item = this.storage[key]
|
||||
const now = (new Date().getTime() / 1000)
|
||||
if (!item) {
|
||||
item = {
|
||||
pointsRemaining: RateLimiter.points,
|
||||
timeReset: now + RateLimiter.timeSpan
|
||||
pointsRemaining: this.points,
|
||||
timeReset: now + this.timeSpan
|
||||
}
|
||||
}
|
||||
if (item.timeReset <= now) {
|
||||
item.timeReset = now + RateLimiter.timeSpan
|
||||
item.pointsRemaining = RateLimiter.points
|
||||
item.timeReset = now + this.timeSpan
|
||||
item.pointsRemaining = this.points
|
||||
}
|
||||
item.pointsRemaining -= value
|
||||
this.storage[key] = item
|
||||
const headers: RateLimitHeaders = {
|
||||
"X-RateLimit-Limit": RateLimiter.points.toFixed(0),
|
||||
"X-RateLimit-Limit": this.points.toFixed(0),
|
||||
"X-RateLimit-Remaining": Math.max(item.pointsRemaining, 0).toFixed(0),
|
||||
"X-RateLimit-Reset": item.timeReset.toFixed(0)
|
||||
}
|
||||
if (item.pointsRemaining < 0) {
|
||||
const res = new ResponseBuilder()
|
||||
const resp = buildRFC7807({
|
||||
type: '/docs/error/rate-limited',
|
||||
status: StatusCode.TOO_MANY_REQUESTS,
|
||||
title: 'You are being rate limited as you have done too many requests to the server'
|
||||
})
|
||||
resp.headers.append('Retry-After', (item.timeReset - now).toFixed(0))
|
||||
objectLoop(headers, (value, key) => {
|
||||
resp.headers.append(key, value)
|
||||
})
|
||||
}, res)
|
||||
res.addHeader('Retry-After', (item.timeReset - now).toFixed(0))
|
||||
res.addHeaders(headers as any)
|
||||
return resp
|
||||
}
|
||||
return headers
|
||||
|
@ -18,18 +18,14 @@ export default class ResponseBuilder {
|
||||
return this
|
||||
}
|
||||
|
||||
private _headers: Headers = new Headers()
|
||||
public headers(headers: HeadersInit ) {
|
||||
if (headers instanceof Headers) {
|
||||
this._headers = headers
|
||||
} else {
|
||||
this._headers = new Headers(headers)
|
||||
}
|
||||
private _headers: Record<string, string> = {}
|
||||
public headers(headers: Record<string, string>) {
|
||||
this._headers = headers
|
||||
return this
|
||||
}
|
||||
|
||||
public addHeader(key: string, value: string) {
|
||||
this._headers.append(key, value)
|
||||
this._headers[key] = value
|
||||
return this
|
||||
}
|
||||
|
||||
@ -41,7 +37,7 @@ export default class ResponseBuilder {
|
||||
}
|
||||
|
||||
public removeHeader(key: string) {
|
||||
this._headers.delete(key)
|
||||
delete this._headers[key]
|
||||
return this
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
* @returns a number if parsing happened correctly or undefined
|
||||
*/
|
||||
function parseNumber(str: string): number | undefined {
|
||||
if (!/^(\d|\.)+$/g.test(str)) {
|
||||
if (!/^-?(\d|\.)+$/g.test(str)) {
|
||||
return undefined
|
||||
}
|
||||
const float = parseFloat(str)
|
||||
@ -53,10 +53,10 @@ export function getParams(data: string) {
|
||||
const lines = data.split('\n').filter((it) => it.startsWith(';') && it.includes('='))
|
||||
// create the config object
|
||||
const obj: Record<string, string | number> = {}
|
||||
// loop through eacj config
|
||||
// loop through each config
|
||||
for (const line of lines) {
|
||||
// get its key and value
|
||||
const [key, value] = line.split('=', 2).map((it) => it.slice(1).trim())
|
||||
const [key, value] = line.slice(1).split(/ *= */, 2).map((it) => it.trim())
|
||||
// sip if it has no key or value
|
||||
if (!key || !value) {
|
||||
continue
|
||||
@ -78,10 +78,10 @@ export function getParams(data: string) {
|
||||
if (offset > 0) {
|
||||
realKey = `${realKey}_${offset}`
|
||||
}
|
||||
// detect key collisions
|
||||
if (obj[realKey] && obj[realKey] !== realValue) {
|
||||
throw new Error(`Key collision ${key}=${realValue} ${realKey}=${obj[realKey]}`)
|
||||
}
|
||||
// detect key collisions (it will never happens with the while above)
|
||||
// if (obj[realKey] && obj[realKey] !== realValue) {
|
||||
// throw new Error(`Key collision ${key}=${realValue} ${realKey}=${obj[realKey]}`)
|
||||
// }
|
||||
// set the value to the key
|
||||
obj[realKey] = realValue
|
||||
// transform the time to a number of seconds
|
||||
|
@ -1,5 +1,3 @@
|
||||
import Client from './Client'
|
||||
|
||||
/**
|
||||
* the Dao is the object that connect the Database or source to the application layer
|
||||
*
|
||||
|
@ -3,7 +3,13 @@ import Passthrough from '../components/Passthrough.astro'
|
||||
import Layout from '../layouts/Layout.astro'
|
||||
import DaoFactory from '../models/DaoFactory'
|
||||
|
||||
const user = await DaoFactory.get('user').get('648f81f857503c7d29465318')
|
||||
const session = DaoFactory.get('session').getSession(Astro.request)
|
||||
|
||||
if (!session) {
|
||||
return Astro.redirect('/')
|
||||
}
|
||||
|
||||
const user = await DaoFactory.get('user').get(session.userId)
|
||||
const list = await DaoFactory.get('apiKey').findAll({
|
||||
user: user!.id
|
||||
})
|
||||
|
@ -7,12 +7,9 @@ import { spawn } from 'node:child_process'
|
||||
import fs from 'node:fs/promises'
|
||||
import os from 'node:os'
|
||||
import path from 'node:path'
|
||||
import StatusCode from '../../../../libs/HTTP/StatusCode'
|
||||
import { buildRFC7807 } from '../../../../libs/RFCs/RFC7807'
|
||||
import RateLimiter from '../../../../libs/RateLimiter'
|
||||
import ResponseBuilder from '../../../../libs/ResponseBuilder'
|
||||
import StatusCode from '../../../../libs/StatusCode'
|
||||
import { getParams } from '../../../../libs/gcodeUtils'
|
||||
import { validateAuth } from '../../../../libs/validateAuth'
|
||||
import DaoFactory from '../../../../models/DaoFactory'
|
||||
|
||||
interface SliceError {
|
||||
@ -20,7 +17,6 @@ interface SliceError {
|
||||
output: Array<string>
|
||||
}
|
||||
|
||||
|
||||
let tmpDir: string
|
||||
|
||||
/**
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { objectOmit } from '@dzeio/object-util'
|
||||
import type { APIRoute } from 'astro'
|
||||
import StatusCode from '../../../../../../libs/HTTP/StatusCode'
|
||||
import { buildRFC7807 } from '../../../../../../libs/RFCs/RFC7807'
|
||||
import StatusCode from '../../../../../../libs/StatusCode'
|
||||
import DaoFactory from '../../../../../../models/DaoFactory'
|
||||
|
||||
export const post: APIRoute = async ({ params, request, locals }) => {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import type { APIRoute } from 'astro'
|
||||
import crypto from 'node:crypto'
|
||||
import StatusCode from '../../../../../../libs/StatusCode'
|
||||
import StatusCode from '../../../../../../libs/HTTP/StatusCode'
|
||||
import DaoFactory from '../../../../../../models/DaoFactory'
|
||||
|
||||
export const post: APIRoute = async ({ params, locals }) => {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import type { APIRoute } from 'astro'
|
||||
import StatusCode from '../../../../libs/HTTP/StatusCode'
|
||||
import { buildRFC7807 } from '../../../../libs/RFCs/RFC7807'
|
||||
import StatusCode from '../../../../libs/StatusCode'
|
||||
|
||||
export const get: APIRoute = async ({ locals }) => {
|
||||
return locals.responseBuilder
|
||||
|
@ -1,7 +1,7 @@
|
||||
---
|
||||
import Layout from '../../layouts/Layout.astro'
|
||||
import { getEntry } from 'astro:content'
|
||||
import StatusCode from '../../libs/StatusCode'
|
||||
import StatusCode from '../../libs/HTTP/StatusCode'
|
||||
|
||||
const page = Astro.params.page
|
||||
|
||||
|
Reference in New Issue
Block a user