update server
This commit is contained in:
1 parent
1e9bb49e29
commit
d6142bca01
5 files changed
+179
-2
No files matched your search
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"semi": false,
|
||||
"singleQuote": true
|
||||
}
|
||||
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"editor.tabSize": 2,
|
||||
"[vue]": {
|
||||
"editor.defaultFormatter": "octref.vetur",
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true
|
||||
}
|
||||
}
|
||||
+6
-2
@@ -11,6 +11,7 @@ import fastifySwagger from 'fastify-swagger'
|
||||
|
||||
import { db, isDev } from '../shared'
|
||||
import { searchRouter } from './search'
|
||||
import { userRouter } from './user'
|
||||
|
||||
const apiRouter: FastifyPluginAsync = async (f) => {
|
||||
f.register(fCookie)
|
||||
@@ -129,10 +130,15 @@ const apiRouter: FastifyPluginAsync = async (f) => {
|
||||
f.register(searchRouter, {
|
||||
prefix: '/search',
|
||||
})
|
||||
|
||||
f.register(userRouter, {
|
||||
prefix: '/user',
|
||||
})
|
||||
}
|
||||
|
||||
export default apiRouter
|
||||
|
||||
if (require.main === module) {
|
||||
async function main() {
|
||||
const app = fastify({
|
||||
logger: {
|
||||
@@ -158,7 +164,5 @@ async function main() {
|
||||
|
||||
return app.listen(process.env['PORT']!, '0.0.0.0')
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
main()
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import S from 'jsonschema-definer'
|
||||
|
||||
export const sTypeLevel = S.string().enum('character', 'vocabulary')
|
||||
export const sType = S.anyOf(sTypeLevel, S.string().enum('sentence'))
|
||||
|
||||
export const sLevelBrowserByType = S.shape({
|
||||
whatToShow: S.list(S.string()),
|
||||
})
|
||||
|
||||
const oLevelBrowser: Record<
|
||||
typeof sTypeLevel.type,
|
||||
typeof sLevelBrowserByType
|
||||
> = {
|
||||
character: sLevelBrowserByType,
|
||||
vocabulary: sLevelBrowserByType,
|
||||
}
|
||||
|
||||
export const sLevelBrowser = S.shape(oLevelBrowser)
|
||||
|
||||
const oUserLocalSettings = {
|
||||
level: S.integer(),
|
||||
levelMin: S.integer().optional(),
|
||||
level_browser: sLevelBrowser.optional(),
|
||||
}
|
||||
|
||||
const oUserSettings = {
|
||||
...oUserLocalSettings,
|
||||
identifier: S.string(),
|
||||
}
|
||||
|
||||
export const sUserLocalSettings = S.shape(oUserLocalSettings)
|
||||
export const sUserSettings = S.shape(oUserSettings)
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
import { SQLQuery, sql } from '@databases/pg'
|
||||
import { FastifyPluginAsync } from 'fastify'
|
||||
import S from 'jsonschema-definer'
|
||||
|
||||
import { db } from '../shared'
|
||||
import { sUserSettings } from './types'
|
||||
|
||||
export const userRouter: FastifyPluginAsync = async (f) => {
|
||||
{
|
||||
const sQuery = S.shape({
|
||||
select: S.string(),
|
||||
})
|
||||
|
||||
const sResponse = sUserSettings.partial()
|
||||
|
||||
f.get<{
|
||||
Querystring: typeof sQuery.type
|
||||
}>(
|
||||
'/',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'userGetSettings',
|
||||
querystring: sQuery.valueOf(),
|
||||
response: {
|
||||
200: sResponse.valueOf(),
|
||||
},
|
||||
},
|
||||
},
|
||||
async (req): Promise<typeof sResponse.type> => {
|
||||
const select = req.query.select.split(
|
||||
','
|
||||
) as (keyof typeof sUserSettings.type)[]
|
||||
|
||||
const userId: string = req.session['userId']
|
||||
if (!userId) {
|
||||
throw { statusCode: 403 }
|
||||
}
|
||||
|
||||
const selMap: Record<keyof typeof sUserSettings.type, SQLQuery> = {
|
||||
identifier: sql`"identifier"`,
|
||||
level: sql`"level"`,
|
||||
levelMin: sql`"levelMin"`,
|
||||
level_browser: sql`"level_browser"`,
|
||||
}
|
||||
|
||||
const sel = select.map((s) => selMap[s]).filter((s) => s)
|
||||
|
||||
if (!sel.length) {
|
||||
throw { statusCode: 400, message: 'not enough select' }
|
||||
}
|
||||
|
||||
const [r] = await db.query(
|
||||
sql`
|
||||
SELECT ${sql.join(sel, ',')} FROM "user" WHERE "id" = ${userId}
|
||||
`
|
||||
)
|
||||
if (!r) {
|
||||
throw { statusCode: 403 }
|
||||
}
|
||||
|
||||
return {
|
||||
identifier: r.identifier || undefined,
|
||||
level: r.level || undefined,
|
||||
levelMin: r.levelMin || undefined,
|
||||
level_browser: r.levelBrowser || undefined,
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
const sBody = sUserSettings.partial()
|
||||
|
||||
const sResult = S.shape({
|
||||
result: S.string(),
|
||||
})
|
||||
|
||||
f.patch<{
|
||||
Body: Partial<typeof sUserSettings.type>
|
||||
}>(
|
||||
'/',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'userUpdateSettings',
|
||||
body: sBody.valueOf(),
|
||||
response: {
|
||||
201: sResult.valueOf(),
|
||||
},
|
||||
},
|
||||
},
|
||||
async (req, reply): Promise<typeof sResult.type> => {
|
||||
const userId: string = req.session['userId']
|
||||
if (!userId) {
|
||||
throw { statusCode: 403 }
|
||||
}
|
||||
|
||||
if (!Object.keys(req.body).length) {
|
||||
throw { statusCode: 400, message: 'not enough update' }
|
||||
}
|
||||
|
||||
const uMap: Partial<Record<keyof typeof sUserSettings.type, SQLQuery>> =
|
||||
{
|
||||
level: sql`"level" = ${req.body.level}`,
|
||||
levelMin: sql`"levelMin" = ${req.body.levelMin}`,
|
||||
level_browser: sql`"level_browser" = ${req.body.level_browser}`,
|
||||
}
|
||||
|
||||
await db.tx(async (db) => {
|
||||
await db.query(sql`
|
||||
UPDATE "user"
|
||||
SET ${sql.join(
|
||||
Object.keys(req.body).map((k) => (uMap as any)[k]),
|
||||
','
|
||||
)}
|
||||
WHERE "id" = ${userId}
|
||||
`)
|
||||
})
|
||||
|
||||
reply.status(201)
|
||||
return {
|
||||
result: 'updated',
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user