feat: continue work
Signed-off-by: Avior <github@avior.me>
This commit is contained in:
parent
d76f412b82
commit
9530be5c43
22
.dockerignore
Normal file
22
.dockerignore
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# build output
|
||||||
|
dist/
|
||||||
|
|
||||||
|
# generated types
|
||||||
|
.astro/
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# logs
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
|
||||||
|
# macOS-specific files
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
slicers/*
|
||||||
|
|
||||||
|
# Coverage
|
||||||
|
coverage/
|
12
Dockerfile
12
Dockerfile
@ -3,7 +3,7 @@
|
|||||||
#########
|
#########
|
||||||
# Build #
|
# Build #
|
||||||
#########
|
#########
|
||||||
FROM node:alpine as BUILD_IMAGE
|
FROM node:20-alpine as BUILD_IMAGE
|
||||||
|
|
||||||
# External deps (for node-gyp add: "python3 make g++")
|
# External deps (for node-gyp add: "python3 make g++")
|
||||||
# git is used to install the npm packages with git deps
|
# git is used to install the npm packages with git deps
|
||||||
@ -32,12 +32,11 @@ RUN npm prune --omit=dev
|
|||||||
##############
|
##############
|
||||||
# Production #
|
# Production #
|
||||||
##############
|
##############
|
||||||
FROM node:latest as PROD_IMAGE
|
FROM node:20-slim as PROD_IMAGE
|
||||||
|
|
||||||
# inform software to be in production
|
# inform software to be in production
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
ENV HOST=0.0.0.0
|
ENV HOST=0.0.0.0
|
||||||
ENV CONFIGS_PATH=./configs
|
|
||||||
|
|
||||||
# Download & Install Slic3r
|
# Download & Install Slic3r
|
||||||
# RUN apt-get update \
|
# RUN apt-get update \
|
||||||
@ -47,13 +46,13 @@ ENV CONFIGS_PATH=./configs
|
|||||||
# ENV SLICER_PATH slic3r
|
# ENV SLICER_PATH slic3r
|
||||||
|
|
||||||
# Download & install PrusaSlicer
|
# Download & install PrusaSlicer
|
||||||
ADD https://github.com/prusa3d/PrusaSlicer/releases/download/version_2.6.0/PrusaSlicer-2.6.0+linux-x64-GTK3-202306191220.tar.bz2 ./
|
|
||||||
RUN tar -xvf PrusaSlicer-2.6.0+linux-x64-GTK3-202306191220.tar.bz2 -C /opt
|
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get install -y --no-install-recommends \
|
&& apt-get install -y --no-install-recommends \
|
||||||
prusa-slicer \
|
prusa-slicer bzip2 \
|
||||||
&& apt-get remove prusa-slicer -y \
|
&& apt-get remove prusa-slicer -y \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
ADD https://github.com/prusa3d/PrusaSlicer/releases/download/version_2.6.0/PrusaSlicer-2.6.0+linux-x64-GTK3-202306191220.tar.bz2 ./
|
||||||
|
RUN tar -xvf PrusaSlicer-2.6.0+linux-x64-GTK3-202306191220.tar.bz2 -C /opt
|
||||||
|
|
||||||
ENV PATH /opt/PrusaSlicer-2.6.0+linux-x64-GTK3-202306191220/bin:$PATH
|
ENV PATH /opt/PrusaSlicer-2.6.0+linux-x64-GTK3-202306191220/bin:$PATH
|
||||||
ENV SLICER_PATH prusa-slicer
|
ENV SLICER_PATH prusa-slicer
|
||||||
@ -71,7 +70,6 @@ WORKDIR /home/node
|
|||||||
|
|
||||||
# copy from build image
|
# copy from build image
|
||||||
COPY --chown=node:node --from=BUILD_IMAGE /home/node/node_modules ./node_modules
|
COPY --chown=node:node --from=BUILD_IMAGE /home/node/node_modules ./node_modules
|
||||||
COPY --chown=node:node --from=BUILD_IMAGE /home/node/configs ./configs
|
|
||||||
COPY --chown=node:node --from=BUILD_IMAGE /home/node/dist ./dist
|
COPY --chown=node:node --from=BUILD_IMAGE /home/node/dist ./dist
|
||||||
COPY --chown=node:node --from=BUILD_IMAGE /home/node/package.json /home/node/.env* ./
|
COPY --chown=node:node --from=BUILD_IMAGE /home/node/package.json /home/node/.env* ./
|
||||||
|
|
||||||
|
10
package-lock.json
generated
10
package-lock.json
generated
@ -12,7 +12,7 @@
|
|||||||
"@astrojs/tailwind": "^3.1.3",
|
"@astrojs/tailwind": "^3.1.3",
|
||||||
"@dzeio/logger": "^3.0.0",
|
"@dzeio/logger": "^3.0.0",
|
||||||
"@dzeio/object-util": "^1.5.0",
|
"@dzeio/object-util": "^1.5.0",
|
||||||
"@dzeio/url-manager": "^1.0.9",
|
"@dzeio/url-manager": "^1.0.10",
|
||||||
"astro": "^2.6.4",
|
"astro": "^2.6.4",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"jsonwebtoken": "^9.0.0",
|
"jsonwebtoken": "^9.0.0",
|
||||||
@ -604,11 +604,11 @@
|
|||||||
"integrity": "sha512-NlKJulgd95Gvca3Wx9BQi0rABzwtvuCe9JDPBe9lOVmDDs2ufEWiTsLq59rtE1BEuuGzIm9wj/+8Imgk1Svfrw=="
|
"integrity": "sha512-NlKJulgd95Gvca3Wx9BQi0rABzwtvuCe9JDPBe9lOVmDDs2ufEWiTsLq59rtE1BEuuGzIm9wj/+8Imgk1Svfrw=="
|
||||||
},
|
},
|
||||||
"node_modules/@dzeio/url-manager": {
|
"node_modules/@dzeio/url-manager": {
|
||||||
"version": "1.0.9",
|
"version": "1.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/@dzeio/url-manager/-/url-manager-1.0.9.tgz",
|
"resolved": "https://registry.npmjs.org/@dzeio/url-manager/-/url-manager-1.0.10.tgz",
|
||||||
"integrity": "sha512-aBERzF4xznhQhHlzv35fp2cl99N+VjM37CL+R5DJiyIJmgmiz56zru1SKq8Xnv2rulF+6QrrnlT6ZulQDdp9EA==",
|
"integrity": "sha512-gy8/0yp60crudcqIwsyFsfbHOnG32TAYg14m+at9wzw/bau5URsarlLkO5A3tigk91m05tZQfHLa4xwrXBSY/w==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@dzeio/object-util": "^1.4.0"
|
"@dzeio/object-util": "^1.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@emmetio/abbreviation": {
|
"node_modules/@emmetio/abbreviation": {
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
"@astrojs/tailwind": "^3.1.3",
|
"@astrojs/tailwind": "^3.1.3",
|
||||||
"@dzeio/logger": "^3.0.0",
|
"@dzeio/logger": "^3.0.0",
|
||||||
"@dzeio/object-util": "^1.5.0",
|
"@dzeio/object-util": "^1.5.0",
|
||||||
"@dzeio/url-manager": "^1.0.9",
|
"@dzeio/url-manager": "^1.0.10",
|
||||||
"astro": "^2.6.4",
|
"astro": "^2.6.4",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"jsonwebtoken": "^9.0.0",
|
"jsonwebtoken": "^9.0.0",
|
||||||
|
11
src/env.d.ts
vendored
11
src/env.d.ts
vendored
@ -1,6 +1,5 @@
|
|||||||
/// <reference path="../.astro/types.d.ts" />
|
/// <reference path="../.astro/types.d.ts" />
|
||||||
/// <reference types="astro/client" />
|
/// <reference types="astro/client" />
|
||||||
/// <reference types="./libs/ResponseBuilder" />
|
|
||||||
|
|
||||||
interface ImportMetaEnv {
|
interface ImportMetaEnv {
|
||||||
PRUSASLICER_PATH?: string
|
PRUSASLICER_PATH?: string
|
||||||
@ -21,6 +20,14 @@ declare namespace App {
|
|||||||
* authentification key is the api key or the session token
|
* authentification key is the api key or the session token
|
||||||
*/
|
*/
|
||||||
authKey?: string
|
authKey?: string
|
||||||
responseBuilder?: ResponseBuilder
|
responseBuilder: {
|
||||||
|
body(body: string | Buffer | object | null | undefined): this
|
||||||
|
headers(headers: HeadersInit ): this
|
||||||
|
addHeader(key: string, value: string): this
|
||||||
|
addHeaders(headers: Record<string, string>): this
|
||||||
|
removeHeader(key: string): this
|
||||||
|
status(status: number): this
|
||||||
|
build(): Response
|
||||||
|
} // ./libs/ResponseBuilder object
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ export default class RateLimiter {
|
|||||||
/**
|
/**
|
||||||
* timeSpan in seconds
|
* timeSpan in seconds
|
||||||
*/
|
*/
|
||||||
public static timeSpan = 600
|
public static timeSpan = 60
|
||||||
|
|
||||||
private static instance: RateLimiter = new RateLimiter()
|
private static instance: RateLimiter = new RateLimiter()
|
||||||
public static getInstance(): RateLimiter {
|
public static getInstance(): RateLimiter {
|
||||||
|
@ -6,10 +6,12 @@ import { objectLoop } from '@dzeio/object-util'
|
|||||||
export default class ResponseBuilder {
|
export default class ResponseBuilder {
|
||||||
|
|
||||||
private _body: BodyInit | null | undefined
|
private _body: BodyInit | null | undefined
|
||||||
public body(body: string | object | null | undefined) {
|
public body(body: string | Buffer | object | null | undefined) {
|
||||||
if (typeof body === 'object') {
|
if (typeof body === 'object' && !(body instanceof Buffer)) {
|
||||||
this._body = JSON.stringify(body)
|
this._body = JSON.stringify(body)
|
||||||
this.addHeader('Content-Type', 'application/json')
|
this.addHeader('Content-Type', 'application/json')
|
||||||
|
} else if (body instanceof Buffer) {
|
||||||
|
this._body = body.toString()
|
||||||
} else {
|
} else {
|
||||||
this._body = body
|
this._body = body
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ import DaoFactory from '../models/DaoFactory'
|
|||||||
import CookieManager from './CookieManager'
|
import CookieManager from './CookieManager'
|
||||||
import { buildRFC7807 } from './RFCs/RFC7807'
|
import { buildRFC7807 } from './RFCs/RFC7807'
|
||||||
|
|
||||||
interface Permission {
|
export interface Permission {
|
||||||
name: string
|
name: string
|
||||||
/**
|
/**
|
||||||
* if set it will be usable by users
|
* if set it will be usable by users
|
||||||
|
@ -1,16 +1,43 @@
|
|||||||
|
import { objectLoop } from '@dzeio/object-util'
|
||||||
|
import URLManager from '@dzeio/url-manager'
|
||||||
import { defineMiddleware } from "astro/middleware"
|
import { defineMiddleware } from "astro/middleware"
|
||||||
import { validateAuth } from '../libs/validateAuth'
|
import { buildRFC7807 } from '../libs/RFCs/RFC7807'
|
||||||
|
import { Permission, validateAuth } from '../libs/validateAuth'
|
||||||
|
|
||||||
|
const endpointsPermissions: Record<string, Permission> = {
|
||||||
|
'/api/v1/users/[userId]/configs/[configId]/files/[fileName]': {
|
||||||
|
api: true,
|
||||||
|
cookie: true,
|
||||||
|
name: 'configs.get'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function objectFind(obj: object, fn: (value: any, key: any) => boolean): {key: string, value: any} | null {
|
||||||
|
let res: {key: string, value: any} | null = null
|
||||||
|
objectLoop(obj, (value, key) => {
|
||||||
|
const tmp = fn(value, key)
|
||||||
|
if (tmp) {
|
||||||
|
res = {
|
||||||
|
key, value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tmp
|
||||||
|
})
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
// `context` and `next` are automatically typed
|
// `context` and `next` are automatically typed
|
||||||
export default defineMiddleware(async (context, next) => {
|
export default defineMiddleware(async (context, next) => {
|
||||||
if (!context.request.url.includes('api')) {
|
if (!context.request.url.includes('api')) {
|
||||||
return next()
|
return next()
|
||||||
}
|
}
|
||||||
const auth = await validateAuth(context.request, {
|
const permission = objectFind(endpointsPermissions, (_, key) => new URLManager(key).toString(context.params as any) === context.url.pathname)
|
||||||
name: 'slicing.slice',
|
if (!permission) {
|
||||||
api: true,
|
return buildRFC7807({
|
||||||
cookie: true
|
type: 'idk'
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
const auth = await validateAuth(context.request, permission.value)
|
||||||
if (typeof auth === 'object') {
|
if (typeof auth === 'object') {
|
||||||
return auth
|
return auth
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,21 @@
|
|||||||
import { defineMiddleware } from "astro/middleware"
|
import { defineMiddleware } from "astro/middleware"
|
||||||
|
import { buildRFC7807 } from '../libs/RFCs/RFC7807'
|
||||||
import ResponseBuilder from '../libs/ResponseBuilder'
|
import ResponseBuilder from '../libs/ResponseBuilder'
|
||||||
|
|
||||||
// `context` and `next` are automatically typed
|
// `context` and `next` are automatically typed
|
||||||
export default defineMiddleware(async (context, next) => {
|
export default defineMiddleware(async ({ request, locals }, next) => {
|
||||||
context.locals.responseBuilder = new ResponseBuilder()
|
locals.responseBuilder = new ResponseBuilder()
|
||||||
|
console.log(`[${new Date().toISOString()}] ${request.headers.get('user-agent')?.slice(0, 32).padEnd(32)} ${request.method.padEnd(7)} ${request.url}`)
|
||||||
|
|
||||||
return next()
|
try {
|
||||||
|
const res = await next()
|
||||||
|
console.log(`[${new Date().toISOString()}] ${request.headers.get('user-agent')?.slice(0, 32).padEnd(32)} ${request.method.padEnd(7)} ${res.status} ${request.url}`)
|
||||||
|
return res
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
return buildRFC7807({
|
||||||
|
type: '/docs/errors/global-error',
|
||||||
|
status: 500
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
import type { APIRoute } from 'astro'
|
|
||||||
import crypto from 'node:crypto'
|
|
||||||
import { validateAuth } from '../../../../../libs/validateAuth'
|
|
||||||
import DaoFactory from '../../../../../models/DaoFactory'
|
|
||||||
|
|
||||||
export const post: APIRoute = async ({ params, request }) => {
|
|
||||||
validateAuth(request, {
|
|
||||||
name: 'keys.create',
|
|
||||||
cookie: true,
|
|
||||||
api: false
|
|
||||||
})
|
|
||||||
const userId = params.userId as string
|
|
||||||
|
|
||||||
const dao = await DaoFactory.get('apiKey').create({
|
|
||||||
user: userId,
|
|
||||||
key: crypto.randomUUID(),
|
|
||||||
permissions: [
|
|
||||||
'admin.user.list'
|
|
||||||
]
|
|
||||||
})
|
|
||||||
return {
|
|
||||||
status: 201,
|
|
||||||
body: JSON.stringify(dao)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
import type { APIRoute } from 'astro'
|
|
||||||
import { validateAuth } from '../../../libs/validateAuth'
|
|
||||||
|
|
||||||
export const get: APIRoute = async ({ params, request }) => {
|
|
||||||
const requestInvalid = await validateAuth(request, {
|
|
||||||
name: 'user.list',
|
|
||||||
api: false,
|
|
||||||
cookie: true
|
|
||||||
})
|
|
||||||
if (requestInvalid) {
|
|
||||||
return requestInvalid
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
body: JSON.stringify({iam: true})
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +1,8 @@
|
|||||||
import { objectOmit } from '@dzeio/object-util'
|
|
||||||
import type { APIRoute } from 'astro'
|
import type { APIRoute } from 'astro'
|
||||||
import { buildRFC7807 } from '../../../../../../../libs/RFCs/RFC7807'
|
import { buildRFC7807 } from '../../../../../../../../libs/RFCs/RFC7807'
|
||||||
import DaoFactory from '../../../../../../../models/DaoFactory'
|
import DaoFactory from '../../../../../../../../models/DaoFactory'
|
||||||
|
|
||||||
export const get: APIRoute = async ({ params, request }) => {
|
export const get: APIRoute = async ({ params, locals }) => {
|
||||||
const userId = params.userId as string
|
|
||||||
const configId = params.configId as string
|
const configId = params.configId as string
|
||||||
const fileName = params.fileName as string
|
const fileName = params.fileName as string
|
||||||
|
|
||||||
@ -19,8 +17,8 @@ export const get: APIRoute = async ({ params, request }) => {
|
|||||||
|
|
||||||
const file = dao.files.find((it) => it.name === fileName)
|
const file = dao.files.find((it) => it.name === fileName)
|
||||||
|
|
||||||
return {
|
return locals.responseBuilder
|
||||||
status: 200,
|
.status(200)
|
||||||
body: file?.data
|
.body(file?.data)
|
||||||
}
|
.build()
|
||||||
}
|
}
|
@ -1,9 +1,10 @@
|
|||||||
import { objectOmit } from '@dzeio/object-util'
|
import { objectOmit } from '@dzeio/object-util'
|
||||||
import type { APIRoute } from 'astro'
|
import type { APIRoute } from 'astro'
|
||||||
import { buildRFC7807 } from '../../../../../libs/RFCs/RFC7807'
|
import { buildRFC7807 } from '../../../../../../libs/RFCs/RFC7807'
|
||||||
import DaoFactory from '../../../../../models/DaoFactory'
|
import StatusCode from '../../../../../../libs/StatusCode'
|
||||||
|
import DaoFactory from '../../../../../../models/DaoFactory'
|
||||||
|
|
||||||
export const post: APIRoute = async ({ params, request }) => {
|
export const post: APIRoute = async ({ params, request, locals }) => {
|
||||||
const userId = params.userId as string
|
const userId = params.userId as string
|
||||||
|
|
||||||
const body = request.body
|
const body = request.body
|
||||||
@ -37,8 +38,8 @@ export const post: APIRoute = async ({ params, request }) => {
|
|||||||
data: buffer
|
data: buffer
|
||||||
}]
|
}]
|
||||||
})
|
})
|
||||||
return {
|
return locals.responseBuilder
|
||||||
status: 201,
|
.status(StatusCode.CREATED)
|
||||||
body: JSON.stringify(objectOmit(dao ?? {}, 'files'))
|
.body(objectOmit(dao ?? {}, 'files'))
|
||||||
}
|
.build()
|
||||||
}
|
}
|
20
src/pages/api/v1/users/[userId]/keys/index.ts
Normal file
20
src/pages/api/v1/users/[userId]/keys/index.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import type { APIRoute } from 'astro'
|
||||||
|
import crypto from 'node:crypto'
|
||||||
|
import StatusCode from '../../../../../../libs/StatusCode'
|
||||||
|
import DaoFactory from '../../../../../../models/DaoFactory'
|
||||||
|
|
||||||
|
export const post: APIRoute = async ({ params, locals }) => {
|
||||||
|
const userId = params.userId as string
|
||||||
|
|
||||||
|
const dao = await DaoFactory.get('apiKey').create({
|
||||||
|
user: userId,
|
||||||
|
key: crypto.randomUUID(),
|
||||||
|
permissions: [
|
||||||
|
'admin.user.list'
|
||||||
|
]
|
||||||
|
})
|
||||||
|
return locals.responseBuilder
|
||||||
|
.status(StatusCode.CREATED)
|
||||||
|
.body(dao)
|
||||||
|
.build()
|
||||||
|
}
|
17
src/pages/api/v1/users/index.ts
Normal file
17
src/pages/api/v1/users/index.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import type { APIRoute } from 'astro'
|
||||||
|
import { buildRFC7807 } from '../../../../libs/RFCs/RFC7807'
|
||||||
|
import StatusCode from '../../../../libs/StatusCode'
|
||||||
|
|
||||||
|
export const get: APIRoute = async ({ locals }) => {
|
||||||
|
return locals.responseBuilder
|
||||||
|
.status(200)
|
||||||
|
.body({iam: true})
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const options: APIRoute = async () => {
|
||||||
|
return buildRFC7807({
|
||||||
|
status: StatusCode.METHOD_NOT_ALLOWED,
|
||||||
|
details: 'Allowed methods: "GET"'
|
||||||
|
})
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user