config husky

This commit is contained in:
Jyotirmoy Bandyopadhayaya 2023-04-20 13:46:57 +05:30
parent 2544ce8031
commit 34e2a94fce
Signed by: bravo68web
GPG Key ID: F5671FD7BCB9917A
13 changed files with 79 additions and 73 deletions

View File

@ -1,9 +1,9 @@
import { authClient } from "../helpers/auth_client"; import { authClient } from '../helpers/auth_client'
import { generators } from 'openid-client'; import { generators } from 'openid-client'
import { configKeys } from ".."; import { configKeys } from '..'
const code_verifier = generators.codeVerifier(); const code_verifier = generators.codeVerifier()
const code_challenge = generators.codeChallenge(code_verifier); const code_challenge = generators.codeChallenge(code_verifier)
export default () => { export default () => {
const authurl = authClient.authorizationUrl({ const authurl = authClient.authorizationUrl({
@ -12,14 +12,11 @@ export default () => {
code_challenge_method: 'S256', code_challenge_method: 'S256',
client_id: configKeys.KEYCLOAK_CLIENT_ID, client_id: configKeys.KEYCLOAK_CLIENT_ID,
redirect_uri: configKeys.KEYCLOAK_REDIRECT_URI, redirect_uri: configKeys.KEYCLOAK_REDIRECT_URI,
}); })
return { return {
authurl, authurl,
}; }
} }
export { export { code_challenge, code_verifier }
code_challenge,
code_verifier,
}

View File

@ -1,32 +1,37 @@
import { NextFunction, Response } from "express"; import { NextFunction, Response } from 'express'
import {authClient} from "../helpers/auth_client"; import { authClient } from '../helpers/auth_client'
import { ModRequest } from "../types"; import { ModRequest } from '../types'
import { CustomError } from "../libs/error"; import { CustomError } from '../libs/error'
const middleware = async (req: ModRequest | any, res: Response, next: NextFunction) => { const middleware = async (
req: ModRequest | any,
res: Response,
next: NextFunction
) => {
try { try {
const authHeader = req.headers?.authorization?.split(' '); const authHeader = req.headers?.authorization?.split(' ')
if (!authHeader) { if (!authHeader) {
throw new Error("No authorization header"); throw new Error('No authorization header')
} }
const token : string = authHeader[1]; const token: string = authHeader[1]
const user = await authClient.userinfo(token, { const user = await authClient.userinfo(token, {
method: 'GET', method: 'GET',
tokenType: 'Bearer', tokenType: 'Bearer',
params: { params: {
access_token: token access_token: token,
}, },
via: 'header' via: 'header',
}); })
req.user = user; req.user = user
next(); next()
} } catch (err) {
catch (err) { next(
next(new CustomError({ new CustomError({
message: "Invalid token", message: 'Invalid token',
statusCode: 401 statusCode: 401,
})) })
)
} }
} }
export default middleware export default middleware

View File

@ -1,10 +1,10 @@
import {authClient} from "../helpers/auth_client"; import { authClient } from '../helpers/auth_client'
import { code_verifier } from './check' import { code_verifier } from './check'
export default async (session_state: string, code: string) => { export default async (session_state: string, code: string) => {
return authClient.callback( return authClient.callback(
'http://localhost:4038/auth/signin/callback', 'http://localhost:4038/auth/signin/callback',
{ code_verifier, code, session_state, expires_in: "1d" }, { code_verifier, code, session_state, expires_in: '1d' },
{ code_verifier } { code_verifier }
); )
} }

View File

@ -1,9 +1,11 @@
import fs from 'fs' import fs from 'fs'
import { parse as parseFile } from 'envfile' import { parse as parseFile } from 'envfile'
import { Issuer } from 'openid-client'; import { Issuer } from 'openid-client'
const keyCloakIssuer : Issuer = await Issuer.discover(process.env.KEYCLOAK_AUTH_SERVER_URL!); const keyCloakIssuer: Issuer = await Issuer.discover(
console.log('🔐 Connected to Keycloak'); process.env.KEYCLOAK_AUTH_SERVER_URL!
)
console.log('🔐 Connected to Keycloak')
type IconfigStore = 'development' | 'production' type IconfigStore = 'development' | 'production'
@ -48,7 +50,7 @@ export interface IConfigKeys {
export default class ConfigStoreFactory { export default class ConfigStoreFactory {
public configStoreType: IconfigStore public configStoreType: IconfigStore
constructor(isProd: boolean = false) { constructor(isProd = false) {
if (isProd) { if (isProd) {
this.configStoreType = 'production' this.configStoreType = 'production'
} else { } else {
@ -57,8 +59,8 @@ export default class ConfigStoreFactory {
} }
public async getConfigStore() { public async getConfigStore() {
const publicKEY = fs.readFileSync('./jwtRS256.key', 'utf8'); const publicKEY = fs.readFileSync('./jwtRS256.key', 'utf8')
const privateKEY = fs.readFileSync('./jwtRS256.key.pub', 'utf8'); const privateKEY = fs.readFileSync('./jwtRS256.key.pub', 'utf8')
if (this.configStoreType === 'development') { if (this.configStoreType === 'development') {
const envContent = await fs.readFileSync(`./.env`, 'utf8') const envContent = await fs.readFileSync(`./.env`, 'utf8')
const env: Partial<IConfigKeys> = await parseFile(envContent) const env: Partial<IConfigKeys> = await parseFile(envContent)
@ -73,8 +75,8 @@ export default class ConfigStoreFactory {
) )
reqEnvContent = reqEnvContent.replaceAll('=', '') reqEnvContent = reqEnvContent.replaceAll('=', '')
reqEnvContent = reqEnvContent.split('\n') reqEnvContent = reqEnvContent.split('\n')
let missingKeys: string[] = [] const missingKeys: string[] = []
let env: Partial<IConfigKeys> = {} const env: Partial<IConfigKeys> = {}
env.PUBLIC_KEY = publicKEY env.PUBLIC_KEY = publicKEY
env.PRIVATE_KEY = privateKEY env.PRIVATE_KEY = privateKEY
env.KEYCLOAK_ISSUER = keyCloakIssuer env.KEYCLOAK_ISSUER = keyCloakIssuer

View File

@ -11,11 +11,14 @@ export default class AuthController {
} }
public callback = async (req: Request, res: Response) => { public callback = async (req: Request, res: Response) => {
const { session_state, code } = req.query as { session_state: string, code: string} const { session_state, code } = req.query as {
session_state: string
code: string
}
res.send(makeResponse(await verify(session_state, code))) res.send(makeResponse(await verify(session_state, code)))
} }
public me = (req: ModRequest, res: Response) => { public me = (req: ModRequest, res: Response) => {
res.send(makeResponse(req.user)) res.send(makeResponse(req.user))
} }
} }

View File

@ -4,11 +4,10 @@ import { Request, Response } from 'express'
export default class MastodonController extends MastodonService { export default class MastodonController extends MastodonService {
public fetchMastodonProfile = async (req: Request, res: Response) => { public fetchMastodonProfile = async (req: Request, res: Response) => {
try{ try {
const data = await this.getMastodonProfile() const data = await this.getMastodonProfile()
return res.send(makeResponse(data)) return res.send(makeResponse(data))
} } catch (err: any) {
catch (err: any){
res.send(makeResponse(err.message, {}, 'Failed', true)) res.send(makeResponse(err.message, {}, 'Failed', true))
} }
} }
@ -17,8 +16,7 @@ export default class MastodonController extends MastodonService {
try { try {
const data = await this.getMastodonStatuses() const data = await this.getMastodonStatuses()
return res.send(makeResponse(data)) return res.send(makeResponse(data))
} } catch (err: any) {
catch (err: any) {
res.send(makeResponse(err.message, {}, 'Failed', true)) res.send(makeResponse(err.message, {}, 'Failed', true))
} }
} }

View File

@ -1,4 +1,4 @@
import { configKeys } from ".."; import { configKeys } from '..'
const { KEYCLOAK_ISSUER } = configKeys const { KEYCLOAK_ISSUER } = configKeys
@ -7,7 +7,9 @@ const authConfig = {
'auth-server-url': configKeys.KEYCLOAK_AUTH_SERVER_URL, 'auth-server-url': configKeys.KEYCLOAK_AUTH_SERVER_URL,
'ssl-required': 'all', 'ssl-required': 'all',
resource: configKeys.KEYCLOAK_CLIENT_ID, resource: configKeys.KEYCLOAK_CLIENT_ID,
credentials: { 'secret-jwt': { secret: configKeys.KEYCLOAK_CLIENT_SECRET } }, credentials: {
'secret-jwt': { secret: configKeys.KEYCLOAK_CLIENT_SECRET },
},
'confidential-port': 0, 'confidential-port': 0,
redirect_uri: configKeys.KEYCLOAK_REDIRECT_URI, redirect_uri: configKeys.KEYCLOAK_REDIRECT_URI,
client_secret: configKeys.KEYCLOAK_CLIENT_SECRET, client_secret: configKeys.KEYCLOAK_CLIENT_SECRET,
@ -16,4 +18,4 @@ const authConfig = {
const authClient = new KEYCLOAK_ISSUER.Client(authConfig) const authClient = new KEYCLOAK_ISSUER.Client(authConfig)
export { authClient } export { authClient }

View File

@ -5,30 +5,30 @@ import morgan from 'morgan'
import helmet from 'helmet' import helmet from 'helmet'
import { hgqlInit } from './helpers' import { hgqlInit } from './helpers'
import cacheClient from './helpers/cache.factory'; import cacheClient from './helpers/cache.factory'
import routes from './routes' import routes from './routes'
import { errorHandler, notFoundHandler } from './libs' import { errorHandler, notFoundHandler } from './libs'
import pkg from './package.json' assert { type: 'json' } import pkg from './package.json' assert { type: 'json' }
import configStore, { IConfigKeys } from './configs'; import configStore, { IConfigKeys } from './configs'
export const app: express.Application = express() export const app: express.Application = express()
console.log('🚀', '@b68/api', 'v' + pkg.version) console.log('🚀', '@b68/api', 'v' + pkg.version)
hgqlInit() hgqlInit()
cacheClient.init(); cacheClient.init()
const isDev: boolean = process.env.NODE_ENV == 'production' const isDev: boolean = process.env.NODE_ENV == 'production'
console.log(isDev ? '🚀 Production Mode' : '👷 Development Mode') console.log(isDev ? '🚀 Production Mode' : '👷 Development Mode')
const configs = new configStore(isDev) const configs = new configStore(isDev)
const configKeys: IConfigKeys = await configs.getConfigStore() as IConfigKeys const configKeys: IConfigKeys = (await configs.getConfigStore()) as IConfigKeys
app.use(cors()) app.use(cors())
app.use(helmet()) app.use(helmet())
app.use(morgan('dev')) app.use(morgan('dev'))
app.use(express.json()) app.use(express.json())
app.use(express.urlencoded({ extended: true, limit: '50mb' })) app.use(express.urlencoded({ extended: true, limit: '50mb' }))
app.set('view engine', 'ejs'); app.set('view engine', 'ejs')
console.log('☄ ', 'Base Route', '/') console.log('☄ ', 'Base Route', '/')

View File

@ -11,8 +11,8 @@ router.get('/signin/callback', authController.callback)
router.get('/me', middleware, authController.me as any) router.get('/me', middleware, authController.me as any)
router.get('/', function(req, res) { router.get('/', function (req, res) {
res.render('pages/auth'); res.render('pages/auth')
}); })
export default router export default router

View File

@ -2,11 +2,10 @@ import { Router } from 'express'
const router = Router() const router = Router()
router.use('/health', router.use('/health', (req, res) => {
(req, res) => {
return res.status(200).json({ return res.status(200).json({
status: "OK", status: 'OK',
app: "B68 API", app: 'B68 API',
request_ip: req.ip, request_ip: req.ip,
uptime: process.uptime(), uptime: process.uptime(),
hrtime: process.hrtime(), hrtime: process.hrtime(),

View File

@ -49,12 +49,12 @@ baseDir = path.resolve(baseDir)
loadRoutes(baseDir) loadRoutes(baseDir)
router.get('/', function(req, res) { router.get('/', function (req, res) {
res.render('pages/index'); res.render('pages/index')
}); })
router.get('/favicon.ico', function(req, res) { router.get('/favicon.ico', function (req, res) {
res.sendFile(path.join(__dirname, '../public', 'favicon.ico')); res.sendFile(path.join(__dirname, '../public', 'favicon.ico'))
}); })
export default router export default router

View File

@ -12,7 +12,7 @@ export default class MastodonService {
const { data } = await axiosInstance.get( const { data } = await axiosInstance.get(
'https://fosstodon.org/api/v1/accounts/109612266657666903/statuses', 'https://fosstodon.org/api/v1/accounts/109612266657666903/statuses',
{ {
timeout: 10000 timeout: 10000,
} }
) )
return data return data

View File

@ -10,4 +10,4 @@ export interface PaginationType {
export interface ModRequest extends Request { export interface ModRequest extends Request {
user: any user: any
} }