npmrun 2 years ago
parent
commit
927da90ee5
  1. 1
      public/style/views/color.css
  2. BIN
      public/upload/00018-4071989711_2023_05_16_1684169231914.png
  3. 5
      route.txt
  4. BIN
      source/db/data.db
  5. 13
      source/route/views/index.ts
  6. 82
      source/route/views/upload/_upload.ts
  7. 125
      source/route/views/upload/index.ts
  8. 73
      template/htmx/path/user.pug
  9. 6
      template/ui/header.pug
  10. 2
      template/views/color.pug

1
public/style/views/color.css

@ -65,6 +65,7 @@
.color_list .color_item .color_toggle_list .color_toggle_item {
display: inline-block;
height: 25px;
padding: 2px 6px;
}

BIN
public/upload/00018-4071989711_2023_05_16_1684169231914.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

5
route.txt

@ -1,6 +1,6 @@
D:\1XYX\pro\hapi-demo\source\route\htmx对应路径:
/home/topuser/Code/@project/hapi-demo/source/route/htmx对应路径:
不需权限 : GET /htmx/path/{path*}
D:\1XYX\pro\hapi-demo\source\route\views对应路径:
/home/topuser/Code/@project/hapi-demo/source/route/views对应路径:
不需权限(提供无需验证): GET /404
不需权限(提供无需验证): GET /
不需权限(提供无需验证): GET /about
@ -11,6 +11,7 @@ D:\1XYX\pro\hapi-demo\source\route\views对应路径:
不需权限 : GET /nav
不需权限(提供无需验证): GET /register
不需权限 : POST /register
需要权限 : POST /upload
需要权限 : GET /user
需要权限 : GET /user/logout
需要权限 : POST /user/del

BIN
source/db/data.db

Binary file not shown.

13
source/route/views/index.ts

@ -2,16 +2,15 @@ import { auth, config, method, route, swagger, validate } from "@noderun/hapi-ro
import { Req, Res, ReturnValue } from "#/global"
import path from "path"
import fs from "fs-extra"
import { baseDir, gFail } from "@/util"
import { baseDir, gFail, sourceDir } from "@/util"
import MarkdownIt from "markdown-it"
export default class Index {
@auth("try")
async index(request: Req, h: Res): ReturnValue {
if (request.auth.isAuthenticated) {
// 登录了
} else {
// 未登录
const isRenderHtmx = Reflect.has(request.query, "htmx")
if (isRenderHtmx) {
return h.view("htmx/path/index.pug", { isLogin: request.auth.isAuthenticated })
}
return h.view("views/index.pug", { isLogin: request.auth.isAuthenticated })
}
@ -29,7 +28,9 @@ export default class Index {
return h.redirect(`/login?next=${encodeURIComponent(request.path)}`).takeover()
}
const md = new MarkdownIt()
var result = md.render("# markdown-it rulezz!")
console.log(path.resolve(baseDir, "./docs/a.md"));
var result = md.render(fs.readFileSync(path.resolve(baseDir, "./docs/a.md"), "utf-8"))
const data = {
md: result
}

82
source/route/views/upload/_upload.ts

@ -0,0 +1,82 @@
import path from "path"
import { gSuccess, gFail, uploadDir, uploadPath } from "@/util"
import { dateTimeFormat } from "@/util/util"
import {fileTypeFromFile} from 'file-type';
const fs = require("fs")
const multiparty = require("multiparty")
function saveFile(file) {
return new Promise(async (resolve, reject) => {
const filename = file.originalFilename
const uploadedPath = file.path
const filetype = await fileTypeFromFile(uploadedPath)
const _file = path.parse(filename)
if (filetype && (filetype.ext == "jpg" || filetype.ext == "png")) {
let _name =
_file.name + "_" + dateTimeFormat(new Date(), "yyyy_MM_dd") + "_" + new Date().getTime() + _file.ext
const dstPath = path.resolve(uploadDir, _name)
fs.rename(uploadedPath, dstPath, function (err) {
if (err) {
console.log("rename error: " + err)
reject()
} else {
resolve(path.resolve("/public/upload/"+_name))
}
})
} else {
fs.unlinkSync(uploadedPath)
reject(new Error(filename + "文件不是图片"))
}
})
}
export default function (payload) {
const form = new multiparty.Form({
uploadDir: uploadDir, //路径需要对应自己的项目更改
/*设置文件保存路径 */
encoding: "utf-8",
/*编码设置 */
maxFilesSize: 20000 * 1024 * 1024,
/*设置文件最大值 20MB */
keepExtensions: true,
/*保留后缀*/
})
return new Promise(async (resolve, reject) => {
form.on("part", function (part) {
console.log(part.filename)
})
form.on("progress", function (bytesReceived, bytesExpected) {
if (bytesExpected === null) {
return
}
var percentComplete = (bytesReceived / bytesExpected) * 100
console.log("the form is " + Math.floor(percentComplete) + "%" + " complete")
})
form.parse(payload, async function (err, fields, files) {
// console.log(err, fields, files);
if (err) {
resolve(err.message)
return
}
const errList = []
const fileList = []
for (let i = 0; i < files.file.length; i++) {
const file = files.file[i]
try {
const dstPath = await saveFile(file)
fileList.push(dstPath)
} catch (error) {
errList.push(error.message)
}
}
if (errList.length) {
resolve(gFail(null, errList.join("\n")))
return
}
// resolve(h.view("views/upload.ejs"));
resolve([...new Set(fileList)])
})
})
}

125
source/route/views/upload/index.ts

@ -0,0 +1,125 @@
import { auth, config, method, route, swagger, validate } from "@noderun/hapi-router"
import UploadFunc from "./_upload"
import path, { resolve } from "path";
import { gFail, uploadDir } from "@/util";
import { fileTypeFromFile, fileTypeFromStream } from "file-type";
import { dateTimeFormat } from "@/util/util";
import fs from "fs-extra";
import { Req, Res } from "#/global";
const multiparty = require("multiparty")
export default class {
@config({
payload: {
maxBytes: 20000 * 1024 * 1024,
output: "stream",
parse: false,
multipart: true,
timeout: false,
allow: ["multipart/form-data", "application/x-www-form-urlencoded"],
},
})
@method("POST")
@auth()
async index(request: Req, h: Res) {
const { id } = request.auth.credentials
const { filelist, fields } = await Save(request.payload)
const result = {}
if (fields["username"] && fields["username"][0]) {
result["username"] = fields["username"][0]
}
if (filelist && filelist[0]) {
result["avatar"] = filelist[0]
}
if (JSON.stringify(result) !== "{}") {
const UserModel = request.getModel("user")
UserModel.update(result, { where: { id } })
}
return h.redirect("/user")
}
}
function saveFile(file) {
return new Promise(async (resolve, reject) => {
const filename = file.originalFilename
const uploadedPath = file.path
const filetype = await fileTypeFromFile(uploadedPath)
const _file = path.parse(filename)
if (filetype && (filetype.ext == "jpg" || filetype.ext == "png")) {
let _name =
_file.name + "_" + dateTimeFormat(new Date(), "yyyy_MM_dd") + "_" + new Date().getTime() + _file.ext
const dstPath = path.resolve(uploadDir, _name)
fs.rename(uploadedPath, dstPath, function (err) {
if (err) {
console.log("rename error: " + err)
reject()
} else {
resolve(path.resolve("/public/upload/" + _name))
}
})
} else {
fs.unlinkSync(uploadedPath)
reject(new Error(filename + "文件不是图片"))
}
})
}
function Save(payload) {
const form = new multiparty.Form({
uploadDir: uploadDir, //路径需要对应自己的项目更改
/*设置文件保存路径 */
encoding: "utf-8",
/*编码设置 */
maxFilesSize: 20000 * 1024 * 1024,
/*设置文件最大值 20MB */
keepExtensions: true,
/*保留后缀*/
})
return new Promise<any>(async (resolve, reject) => {
form.on("part", function (part) {
console.log(1111)
console.log(part.filename)
})
form.on("progress", function (bytesReceived, bytesExpected) {
if (bytesExpected === null) {
return
}
// var percentComplete = (bytesReceived / bytesExpected) * 100
// console.log("the form is " + Math.floor(percentComplete) + "%" + " complete")
})
form.parse(payload, async function (err, fields, files) {
// console.log(err, fields, files);
if (err) {
reject(err.message)
return
}
const errList = []
const fileList = []
if (files && files.file && files.file.length) {
for (let i = 0; i < files.file.length; i++) {
const file = files.file[i]
if (file.originalFilename === "" && file.size === 0) {
const uploadedPath = file.path
fs.unlinkSync(uploadedPath)
continue
}
try {
const dstPath = await saveFile(file)
fileList.push(dstPath)
} catch (error) {
errList.push(error.message)
}
}
}
if (errList.length) {
resolve(gFail(null, errList.join("\n")))
return
}
resolve({
fields,
filelist: [...new Set(fileList)]
})
})
})
}

73
template/htmx/path/user.pug

@ -4,12 +4,67 @@ block var
-title=user.nickname || "Welcome" // 网页标题
title #{user.nickname || "Welcome"}
div user
section.section
.container
if user
div nickname: #{user.nickname || "None"}
div email: #{user.email || "None"}
div username: #{user.username || "None"}
div id: #{user.id || "None"}
form(action="/upload" method="post" enctype="multipart/form-data" style="margin: 0 auto; width: 500px;")
.field
.label 用户名
.control
input.input(type="text" name="username" placeholder="请输入用户名" value=user.username)
.field
.label 头像
.control
.file.is-primary
label.file-label
input.file-input(type="file", name="file")
span.file-cta
span.file-label
| 上传头像
if user.avatar
.field
.control
.image.is-128x128
img.is-rounded(src=user.avatar alt=user.username)
+security
.field.is-grouped
.control
button.button.is-link(type="submit") 提交
.control
button.button.is-link.is-light 取消
form(action="", method="post" style="margin: 0 auto; width: 500px;margin-top:20px")
.field
.label 用户名
.control
input.input(type="text" placeholder="请输入用户名" value=user.username)
.field
.label 昵称
.control
input.input(type="text" placeholder="请输入用户名" value=user.nickname)
.field
.label 邮箱
.control
input.input(type="email" placeholder="请输入邮箱" value=user.email)
.field
.label 手机号
.control
input.input(type="tel" placeholder="请输入手机号" value=user.tel)
.field
.label 头像
.control
.file.is-primary
label.file-label
input.file-input(type="file", name="file")
span.file-cta
span.file-label
| 上传头像
if user.avatar
.field
.control
.image.is-128x128
img.is-rounded(src=user.avatar alt=user.username)
.field.is-grouped
.control
button.button.is-link(type="submit") 提交
.control
button.button.is-link.is-light 取消

6
template/ui/header.pug

@ -10,8 +10,8 @@ nav.is-fixed-top.navbar(role='navigation', aria-label='main navigation', style="
span(aria-hidden='true')
#navbarBasicExample.navbar-menu
.navbar-start
a.navbar-item
| 文档
a.navbar-item(hx-get="/?htmx" hx-push-url="/" hx-trigger="click" hx-target="#single-page" hx-swap="innerHTML")
| 首页
.navbar-item.has-dropdown.is-hoverable
a.navbar-link
| 更多
@ -34,6 +34,8 @@ nav.is-fixed-top.navbar(role='navigation', aria-label='main navigation', style="
else
.navbar-item.has-dropdown.is-hoverable
a.navbar-link
.image.is-28x28(style="margin-right: 8px;")
img.is-rounded(src=user.avatar alt=user.username)
div #{user.username}
.navbar-dropdown.is-right
a.navbar-item(hx-get="/user?htmx" hx-push-url="/user" hx-trigger="click" hx-target="#single-page" hx-swap="innerHTML")

2
template/views/color.pug

@ -43,7 +43,7 @@ block content
div(class="color_item_title") #{item.title}
div(class="color_item_desc") #{item.describe || "暂无描述"}
div(class="color_item")
img(src="/public/image/add.png", alt="添加" title="添加" class="color_add")
//- img(src="/public/image/add.png", alt="添加" title="添加" class="color_add")
div
form(action="POST" method="post")
div(class=".wrapper_input"): input(type="text" tabindex="1" value="sadsa" name="a")

Loading…
Cancel
Save