update server
This commit is contained in:
1 parent
1e9bb49e29
commit
d6142bca01
5 files changed
+181
-4
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
|
||||||
|
}
|
||||||
|
}
|
||||||
+8
-4
@@ -11,6 +11,7 @@ import fastifySwagger from 'fastify-swagger'
|
|||||||
|
|
||||||
import { db, isDev } from '../shared'
|
import { db, isDev } from '../shared'
|
||||||
import { searchRouter } from './search'
|
import { searchRouter } from './search'
|
||||||
|
import { userRouter } from './user'
|
||||||
|
|
||||||
const apiRouter: FastifyPluginAsync = async (f) => {
|
const apiRouter: FastifyPluginAsync = async (f) => {
|
||||||
f.register(fCookie)
|
f.register(fCookie)
|
||||||
@@ -129,11 +130,16 @@ const apiRouter: FastifyPluginAsync = async (f) => {
|
|||||||
f.register(searchRouter, {
|
f.register(searchRouter, {
|
||||||
prefix: '/search',
|
prefix: '/search',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
f.register(userRouter, {
|
||||||
|
prefix: '/user',
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export default apiRouter
|
export default apiRouter
|
||||||
|
|
||||||
async function main() {
|
if (require.main === module) {
|
||||||
|
async function main() {
|
||||||
const app = fastify({
|
const app = fastify({
|
||||||
logger: {
|
logger: {
|
||||||
prettyPrint: isDev,
|
prettyPrint: isDev,
|
||||||
@@ -157,8 +163,6 @@ async function main() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
return app.listen(process.env['PORT']!, '0.0.0.0')
|
return app.listen(process.env['PORT']!, '0.0.0.0')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (require.main === module) {
|
|
||||||
main()
|
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