Skip to content

Commit b255234

Browse files
wip initial template files including index, controller and repo
1 parent 71e3ad7 commit b255234

9 files changed

+324
-0
lines changed

.dockerignore

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#deps folder
2+
node_modules/
3+
4+
#misc files
5+
LICENSE
6+
readme.md
7+
8+
#deps def
9+
package-lock.json
10+
11+
.dockerignore
12+
Dockerfile
13+
config.js

.gitignore

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.DS_Store
2+
node_modules/
3+
config.js
4+
config.ts
5+
config.json
6+
dist/
7+
temp/

.npmrc

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
install-strategy=shallow
2+
engine-strict=true
3+
legacy-peer-deps=true
4+
lockfile-version=3
5+
save=true

Dockerfile

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# ---- Build ----
2+
FROM --platform=linux/amd64 groupclaes/npm AS build
3+
4+
# change the working directory to new exclusive app folder
5+
WORKDIR /usr/src/app
6+
7+
# copy project file
8+
COPY ./ ./
9+
10+
# install esbuild globaly
11+
RUN npm install esbuild -g
12+
13+
# install node packages
14+
RUN npm install
15+
16+
# create esbuild package
17+
RUN esbuild ./index.ts --bundle --platform=node --minify --packages=external --external:'./config' --outfile=index.min.js
18+
19+
20+
# ---- Deps ----
21+
FROM --platform=linux/amd64 groupclaes/npm AS depedencies
22+
23+
# change the working directory to new exclusive app folder
24+
WORKDIR /usr/src/app
25+
26+
# copy package file
27+
COPY package.json ./
28+
29+
# install node packages
30+
RUN npm install
31+
32+
33+
# --- release ---
34+
FROM --platform=linux/amd64 groupclaes/node AS release
35+
36+
# set current user to node
37+
USER node
38+
39+
# change the working directory to new exclusive app folder
40+
WORKDIR /usr/src/app
41+
42+
# copy dependencies
43+
COPY --chown=node:node --from=depedencies /usr/src/app ./
44+
45+
# copy project file
46+
COPY --chown=node:node --from=build /usr/src/app/index.min.js ./
47+
48+
# command to run when intantiate an image
49+
CMD ["node","index.min.js"]

index.ts

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import server from './src'
2+
// config is made available using docker
3+
const cfg = require('./config')
4+
5+
const main = async function () {
6+
const fastify = await server(cfg);
7+
8+
['SIGTERM', 'SIGINT'].forEach(signal => {
9+
process.on(signal, async () => {
10+
await fastify?.close()
11+
process.exit(0)
12+
})
13+
})
14+
}
15+
16+
main()

package.json

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"name": "fastify-elastic-backend",
3+
"version": "1.0.0",
4+
"author": "Group Claes",
5+
"license": "MIT",
6+
"scripts": {
7+
"test": "tap --reporter=list --show-full-coverage"
8+
},
9+
"dependencies": {
10+
"@groupclaes/fastify-elastic": "^4.2.1",
11+
"fastify": "^4.25.1",
12+
"jose": "^5.1.3",
13+
"mssql": "^10.0.1"
14+
},
15+
"devDependencies": {
16+
"@types/mssql": "^9.1.4",
17+
"@types/node": "^20.10.5",
18+
"tap": "^18.6.1"
19+
}
20+
}

src/controllers/default.controller.ts

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify'
2+
import { JWTPayload } from 'jose'
3+
import sql from 'mssql'
4+
5+
import DefaultRepository from '../repositories/default.repository'
6+
7+
declare module 'fastify' {
8+
export interface FastifyInstance {
9+
getSqlPool: (name?: string) => Promise<sql.ConnectionPool>
10+
}
11+
12+
export interface FastifyRequest {
13+
jwt: JWTPayload
14+
hasRole: (role: string) => boolean
15+
hasPermission: (permission: string, scope?: string) => boolean
16+
}
17+
18+
export interface FastifyReply {
19+
success: (data?: any, code?: number, executionTime?: number) => FastifyReply
20+
fail: (data?: any, code?: number, executionTime?: number) => FastifyReply
21+
error: (message?: string, code?: number, executionTime?: number) => FastifyReply
22+
}
23+
}
24+
25+
export default async function (fastify: FastifyInstance) {
26+
/**
27+
* Get all default for current user
28+
* @route GET /api/{APP_VERSION}/{serviceName}/default
29+
*/
30+
fastify.get('', async (request: FastifyRequest<{}>, reply: FastifyReply) => {
31+
try {
32+
if (!request.jwt)
33+
return reply.error('missing jwt!', 401)
34+
35+
const pool = await fastify.getSqlPool()
36+
const repo = new DefaultRepository(request.log, pool)
37+
38+
request.log.debug({}, 'fetching default')
39+
const data = await repo.list(request.jwt.sub)
40+
41+
request.log.debug({ default_length: data?.length }, 'fetched default')
42+
return reply.success(data)
43+
} catch (err) {
44+
request.log.error({ err }, 'Failed to fetch default from database')
45+
return reply.error('failed to fetch default from database')
46+
}
47+
})
48+
49+
/**
50+
* Create a new default for user
51+
* @route GET /api/{APP_VERSION}/{serviceName}/default
52+
*/
53+
fastify.get('', async (request: FastifyRequest<{}>, reply: FastifyReply) => {
54+
try {
55+
if (!request.jwt)
56+
return reply.error('missing jwt!', 401)
57+
58+
const pool = await fastify.getSqlPool()
59+
const repo = new DefaultRepository(request.log, pool)
60+
61+
request.log.debug({}, 'creating default')
62+
const data = await repo.create(request.jwt.sub, request.body)
63+
64+
request.log.debug({ success: data }, 'created default')
65+
return reply.success(data)
66+
} catch (err) {
67+
request.log.error({ err }, 'Failed to create default from database')
68+
return reply.error('failed to create default from database')
69+
}
70+
})
71+
72+
/**
73+
* Update a specific user default
74+
* @route put /api/{APP_VERSION}/{serviceName}/default/:id
75+
*/
76+
fastify.put('/:id', async (request: FastifyRequest<{}>, reply: FastifyReply) => {
77+
try {
78+
if (!request.jwt)
79+
return reply.error('missing jwt!', 401)
80+
81+
const pool = await fastify.getSqlPool()
82+
const repo = new DefaultRepository(request.log, pool)
83+
84+
request.log.debug({}, 'updating default')
85+
const data = await repo.update(+request.params.id, request.jwt.sub, request.body)
86+
87+
request.log.debug({ success: data }, 'updated default')
88+
return reply.success(data)
89+
} catch (err) {
90+
request.log.error({ err }, 'Failed to update default in database')
91+
return reply.error('failed to update default in database')
92+
}
93+
})
94+
95+
/**
96+
* Delete a specific user default
97+
* @route DELETE /api/{APP_VERSION}/{serviceName}/default/:id
98+
*/
99+
fastify.delete('/:id', async (request: FastifyRequest<{ Params: { id: number }}>, reply: FastifyReply) => {
100+
try {
101+
if (!request.jwt)
102+
return reply.error('missing jwt!', 401)
103+
104+
const pool = await fastify.getSqlPool()
105+
const repo = new DefaultRepository(request.log, pool)
106+
107+
request.log.debug({}, 'deleting default')
108+
const data = await repo.delete(+request.params.id, request.jwt.sub)
109+
110+
request.log.debug({ success: data }, 'removed default')
111+
return reply.success(data)
112+
} catch (err) {
113+
request.log.error({ err }, 'Failed to delete default from database')
114+
return reply.error('failed to delete default from database')
115+
}
116+
})
117+
}

src/index.ts

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import Fastify from '@groupclaes/fastify-elastic'
2+
import { FastifyInstance } from 'fastify'
3+
import { env } from 'process'
4+
5+
import defaultController from './controllers/default.controller'
6+
7+
const LOGLEVEL = 'debug'
8+
9+
export default async function (config: any): Promise<FastifyInstance | undefined> {
10+
if (!config || !config.wrapper) return
11+
12+
const fastify = await Fastify(config.wrapper)
13+
// prefix used; default to /api/test
14+
const version_prefix = '/api' + (env.APP_VERSION ? '/' + env.APP_VERSION : '')
15+
// registration of controllers
16+
await fastify.register(defaultController, { prefix: `${version_prefix}/${config.wrapper.serviceName}/default`, logLevel: LOGLEVEL })
17+
await fastify.listen({ port: +(env['PORT'] ?? 80), host: '::' })
18+
19+
return fastify
20+
}
+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import sql from 'mssql'
2+
import { FastifyBaseLogger } from 'fastify'
3+
4+
export default class DefaultRepository {
5+
schema: string = 'dbo.'
6+
_logger: FastifyBaseLogger
7+
_pool: sql.ConnectionPool
8+
9+
constructor(logger: FastifyBaseLogger, pool: sql.ConnectionPool) {
10+
this._logger = logger
11+
this._pool = pool
12+
}
13+
14+
async list(user_id?: string): Promise<any[]> {
15+
const r = new sql.Request(this._pool)
16+
r.input('user_id', sql.Int, user_id)
17+
const result = await r.execute(this.schema + 'procedure_name').catch(err => {
18+
this._logger.error({ err }, 'error while executing sql procedure')
19+
})
20+
21+
if (!result)
22+
return []
23+
24+
this._logger.debug({ result }, `Exeecuting procedure ${this.schema}procedure_name result`)
25+
26+
return result.recordset.length > 0 ? result.recordset[0] : []
27+
}
28+
29+
async create(data: any, user_id?: string): Promise<boolean> {
30+
const r = new sql.Request(this._pool)
31+
r.input('user_id', sql.Int, user_id)
32+
r.input('data_name', sql.VarChar, data.name)
33+
const result = await r.execute(this.schema + 'procedure_name').catch(err => {
34+
this._logger.error({ err }, 'error while executing sql procedure')
35+
})
36+
37+
if (!result)
38+
return false
39+
40+
this._logger.debug({ result }, `Exeecuting procedure ${this.schema}procedure_name result`)
41+
42+
return result.rowsAffected[0] > 0
43+
}
44+
45+
async update(id: number, data: any, user_id?: string): Promise<boolean> {
46+
const r = new sql.Request(this._pool)
47+
r.input('user_id', sql.Int, user_id)
48+
r.input('id', sql.Int, id)
49+
r.input('data_name', sql.VarChar, data.name)
50+
const result = await r.execute(this.schema + 'procedure_name').catch(err => {
51+
this._logger.error({ err }, 'error while executing sql procedure')
52+
})
53+
54+
if (!result)
55+
return false
56+
57+
this._logger.debug({ result }, `Exeecuting procedure ${this.schema}procedure_name result`)
58+
59+
return result.rowsAffected[0] > 0
60+
}
61+
62+
async delete(id: number, user_id?: string): Promise<boolean> {
63+
const r = new sql.Request(this._pool)
64+
r.input('user_id', sql.Int, user_id)
65+
r.input('id', sql.Int, id)
66+
const result = await r.execute(this.schema + 'procedure_name').catch(err => {
67+
this._logger.error({ err }, 'error while executing sql procedure')
68+
})
69+
70+
if (!result)
71+
return false
72+
73+
this._logger.debug({ result }, `Exeecuting procedure ${this.schema}procedure_name result`)
74+
75+
return result.rowsAffected[0] > 0
76+
}
77+
}

0 commit comments

Comments
 (0)