Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
students
travel
Commits
d1f8541e
Commit
d1f8541e
authored
Jul 22, 2022
by
Kim, MinGyu
Browse files
admin 페이지 개선
parent
39e4b1fd
Changes
14
Show whitespace changes
Inline
Side-by-side
.gitignore
View file @
d1f8541e
...
...
@@ -3,3 +3,4 @@ node_modules/
package-lock.json
dist/
uploads/
adminpics
\ No newline at end of file
frontend/src/App.tsx
View file @
d1f8541e
...
...
@@ -2,7 +2,15 @@ import React from "react";
import
{
BrowserRouter
,
Route
,
Routes
}
from
"
react-router-dom
"
;
import
"
tailwindcss/tailwind.css
"
;
import
{
IntoPost
}
from
"
./post/intopost
"
;
import
{
Login
,
Profile
,
RequireAuth
,
Signup
,
Admin
,
ImgRewrite
}
from
"
./auth
"
;
import
{
Login
,
Profile
,
RequireAuth
,
Signup
,
Admin
,
ImgRewrite
,
Search
,
}
from
"
./auth
"
;
import
{
Header
,
Body
}
from
"
./home
"
;
import
{
Board
}
from
"
./board
"
;
import
Posting
from
"
./post/posting
"
;
...
...
@@ -39,7 +47,8 @@ export const App = () => {
}
/>
<
Route
path
=
"admin"
element
=
{
<
Admin
/>
}
/>
<
Route
path
=
"rewrite"
element
=
{
<
ImgRewrite
/>
}
/>
<
Route
path
=
"rewrite"
element
=
{
<
ImgRewrite
/>
}
/>
<
Route
path
=
"search"
element
=
{
<
Search
/>
}
/>
</
Route
>
</
Route
>
</
Routes
>
...
...
frontend/src/apis/mainimg.api.ts
View file @
d1f8541e
...
...
@@ -2,12 +2,12 @@ import axios from "axios";
import
{
MainimgType
}
from
"
../types
"
;
import
baseUrl
from
"
./baseUrl
"
;
export
const
mainimg
=
async
(
mainimg
:
MainimgType
)
=>
{
const
{
data
}
=
await
axios
.
post
(
`
${
baseUrl
}
/mainimg`
,
mainimg
);
export
const
mainimg
=
async
(
formdata
:
FormData
)
=>
{
const
{
data
}
=
await
axios
.
post
(
`
${
baseUrl
}
/mainimg`
,
formdata
);
return
data
;
};
export
const
delmainimg
=
async
(
_id
:
string
)
=>
{
export
const
delmainimg
=
async
(
_id
:
string
)
=>
{
const
{
data
}
=
await
axios
.
delete
(
`
${
baseUrl
}
/mainimg/
${
_id
}
`
);
return
data
;
};
...
...
frontend/src/auth/admin.tsx
View file @
d1f8541e
import
React
,
{
FormEvent
,
useEffect
,
useState
,
MouseEvent
}
from
"
react
"
;
import
{
Link
}
from
"
react-router-dom
"
;
import
{
mainimgApi
}
from
"
../apis
"
;
import
{
picture
}
from
"
../apis/profile.api
"
;
import
{
catchErrors
}
from
"
../helpers
"
;
import
{
MainimgType
}
from
"
../types
"
;
import
{
MySlide
}
from
"
./adminslide
"
;
...
...
@@ -11,50 +12,57 @@ export default function Admin() {
async
function
imgsData
()
{
const
imgs
=
await
mainimgApi
.
getmainimg
();
setGetimgs
(
imgs
)
}
;
setGetimgs
(
imgs
)
;
}
useEffect
(()
=>
{
imgsData
();
},
[]);
console
.
log
(
getimgs
);
// 이미지 추가하기
const
[
addimg
,
setAddimg
]
=
useState
<
MainimgType
>
({
_id
:
""
,
theme
:
""
,
city
:
""
,
url
:
""
,
title
:
""
,
pic
:
{
originalfilename
:
""
,
newfilename
:
""
},
});
const
[
loading
,
setLoading
]
=
useState
(
false
);
const
[
error
,
setError
]
=
useState
(
""
);
const
[
addSuccess
,
setAddSuccess
]
=
useState
(
false
);
const
[
delSuccess
,
setDelSuccess
]
=
useState
(
false
);
const
[
file
,
setFile
]
=
useState
<
File
>
();
function
handleSelectChange
(
event
:
React
.
ChangeEvent
<
HTMLSelectElement
>
)
{
const
{
name
,
value
}
=
event
.
currentTarget
;
console
.
log
(
value
);
setAddimg
({
...
addimg
,
[
name
]:
value
});
}
function
handleInputeChange
(
event
:
React
.
ChangeEvent
<
HTMLInputElement
>
)
{
const
{
name
,
value
}
=
event
.
currentTarget
;
setAddimg
({
...
addimg
,
[
name
]:
value
});
}
function
handleFileChange
(
e
:
React
.
ChangeEvent
<
HTMLInputElement
>
)
{
const
file
=
e
.
target
.
files
?.[
0
];
if
(
!
(
file
===
undefined
))
{
setFile
(
file
);
}
}
async
function
handleSubmit
(
event
:
FormEvent
)
{
event
.
preventDefault
();
try
{
setError
(
""
);
console
.
log
(
"
img data
"
,
addimg
);
setLoading
(
true
);
const
res
=
await
mainimgApi
.
mainimg
(
addimg
);
console
.
log
(
"
서버연결됬나요
"
,
res
);
setAddSuccess
(
true
);
setError
(
""
);
}
catch
(
error
)
{
console
.
log
(
"
에러발생
"
);
catchErrors
(
error
,
setError
);
}
finally
{
setLoading
(
false
);
const
formdata
=
new
FormData
();
console
.
log
(
addimg
);
formdata
.
append
(
"
city
"
,
addimg
.
city
);
formdata
.
append
(
"
theme
"
,
addimg
.
theme
);
formdata
.
append
(
"
title
"
,
addimg
.
title
);
if
(
!
(
file
===
undefined
))
{
formdata
.
append
(
"
mainimg
"
,
file
);
console
.
log
(
formdata
);
const
res
=
await
mainimgApi
.
mainimg
(
formdata
);
console
.
log
(
"
확인 중
"
,
res
);
}
}
...
...
@@ -66,7 +74,7 @@ export default function Admin() {
try
{
if
(
confirm
(
"
삭제하시겠습니까?
"
)
==
true
)
{
const
picId
=
event
.
currentTarget
.
id
;
console
.
log
(
"
picId :
"
,
picId
)
console
.
log
(
"
picId :
"
,
picId
)
;
const
res
=
await
mainimgApi
.
delmainimg
(
picId
);
console
.
log
(
"
delete img
"
,
res
);
setDelSuccess
(
true
);
...
...
@@ -74,14 +82,13 @@ export default function Admin() {
}
else
{
return
false
;
}
}
catch
(
error
)
{
}
catch
(
error
)
{
console
.
log
(
"
에러발생
"
);
catchErrors
(
error
,
setError
);
}
finally
{
setLoading
(
false
);
}
;
}
;
}
}
if
(
delSuccess
)
{
alert
(
"
img 삭제되었습니다
"
);
}
...
...
@@ -89,37 +96,41 @@ export default function Admin() {
let
limit
=
15
;
const
numPages
=
Math
.
ceil
(
getimgs
.
length
/
15
);
const
slides
=
[]
const
slides
=
[]
;
for
(
let
i
=
0
;
i
<
numPages
;
i
++
)
{
const
k
=
[
getimgs
.
slice
(
i
*
limit
,
i
*
limit
+
limit
).
map
((
pic
,
index
:
number
)
=>
(
getimgs
.
slice
(
i
*
limit
,
i
*
limit
+
limit
)
.
map
((
picture
,
index
:
number
)
=>
(
<
div
key
=
{
index
}
>
<
div
className
=
{
`m-1 shrink-0 bg-gray-200 rounded shadow-md `
}
>
<
img
src
=
{
pic
.
ur
l
}
src
=
{
"
http://localhost:3000/images/
"
+
pic
t
ur
e
.
pic
.
newfilename
}
className
=
"w-full h-10 md:h-20 object-center"
/>
<
p
className
=
"text-center text-xs"
>
{
pic
.
title
}
</
p
>
<
p
className
=
"text-center text-xs"
>
{
pic
ture
.
title
}
</
p
>
</
div
>
<
div
className
=
"text-end"
>
<
button
className
=
"border-r-2 border-r-indigo-500 text-xs"
>
<
Link
to
=
"/rewrite"
>
수정
</
Link
>
<
Link
to
=
"/rewrite"
>
수정
</
Link
>
</
button
>
<
button
id
=
{
pic
.
_id
}
onClick
=
{
handleDeleteClick
}
className
=
"text-xs"
>
<
button
id
=
{
picture
.
_id
}
onClick
=
{
handleDeleteClick
}
className
=
"text-xs"
>
삭제
</
button
>
</
div
>
</
div
>
))]
)),
];
slides
.
push
(
k
);
}
return
(
<
div
>
<
form
onSubmit
=
{
handleSubmit
}
>
<
form
onSubmit
=
{
handleSubmit
}
>
<
div
className
=
"flex flex-wrap justify-center gap-3"
>
<
div
className
=
"gap-3 md:flex "
>
<
select
...
...
@@ -163,15 +174,23 @@ export default function Admin() {
<
option
value
=
"사이클링"
>
사이클링
</
option
>
</
select
>
<
div
className
=
"flex items-center justify-end gap-3"
>
<
p
>
url :
</
p
>
<
input
name
=
"url"
className
=
"border-2 border-sky-500"
onChange
=
{
handleInputeChange
}
/>
{
/* type="file"/> */
}
<
input
type
=
"file"
id
=
"files"
className
=
"hidden"
onChange
=
{
handleFileChange
}
></
input
>
<
label
htmlFor
=
"files"
className
=
"border-2 m-5"
>
이미지 선택
</
label
>
</
div
>
<
div
className
=
"flex items-center justify-end gap-3 mt-2 md:mt-0"
>
<
p
>
title :
</
p
>
<
input
name
=
"title"
className
=
"border-2 border-sky-500"
onChange
=
{
handleInputeChange
}
/>
<
input
name
=
"title"
className
=
"border-2 border-sky-500"
onChange
=
{
handleInputeChange
}
/>
</
div
>
</
div
>
<
div
className
=
"my-5 flex items-center"
>
...
...
@@ -180,11 +199,8 @@ export default function Admin() {
</
div
>
</
form
>
<
div
className
=
"flex justify-center"
>
<
MySlide
key
=
{
Math
.
random
()
}
slides
=
{
slides
}
/>
<
MySlide
key
=
{
Math
.
random
()
}
slides
=
{
slides
}
/>
</
div
>
</
div
>
);
}
;
}
frontend/src/auth/index.tsx
View file @
d1f8541e
...
...
@@ -3,4 +3,5 @@ export { default as Signup } from "./signup";
export
{
default
as
Profile
}
from
"
./profile
"
;
export
{
RequireAuth
}
from
"
./RequireAuth
"
;
export
{
default
as
Admin
}
from
"
./admin
"
;
export
{
default
as
ImgRewrite
}
from
"
./imgrewrite
"
export
{
default
as
ImgRewrite
}
from
"
./imgrewrite
"
;
export
{
default
as
Search
}
from
"
./search
"
;
frontend/src/auth/search.tsx
0 → 100644
View file @
d1f8541e
import
React
from
"
react
"
;
import
{
useLocation
}
from
"
react-router-dom
"
;
export
default
function
Search
()
{
const
a
=
useLocation
().
state
;
return
(
<
div
>
<
div
></
div
>
</
div
>
);
}
frontend/src/home/header.tsx
View file @
d1f8541e
...
...
@@ -6,6 +6,13 @@ import "tailwindcss/tailwind.css";
export
default
function
Header
()
{
const
{
logout
}
=
useAuth
();
const
[
search
,
setSearch
]
=
useState
(
""
);
const
handleChange
=
(
e
:
React
.
ChangeEvent
<
HTMLInputElement
>
)
=>
{
const
newvalue
=
e
.
target
.
value
;
setSearch
(
newvalue
);
};
return
(
<
div
className
=
"flex flex-col "
>
<
div
className
=
"flex py-10 "
>
...
...
@@ -18,9 +25,12 @@ export default function Header() {
<
input
className
=
"md:ml-20 placeholder:text-white focus:outline-none focus:border-y-4 focus:border-l-4 focus:border-sky-500 md:placeholder:text-slate-400 w-20 md:w-1/2 border-y-4 border-l-4 border-sky-300 pl-9 rounded-l-full focus:border-0"
placeholder
=
"어디로 여행가고 싶나요?"
onChange
=
{
handleChange
}
/>
<
button
className
=
"shrink-0 border-y-4 border-r-4 border-sky-500 rounded-r-full pr-4"
>
{
/* <Link to="/search" state={}>
검색
</Link> */
}
</
button
>
<
div
className
=
"shrink-0 p-3 md:ml-40 h-12"
>
...
...
frontend/src/types/index.tsx
View file @
d1f8541e
...
...
@@ -43,6 +43,9 @@ export interface MainimgType {
_id
:
string
;
theme
:
string
;
city
:
string
;
url
:
string
;
title
:
string
;
pic
:
{
originalfilename
:
string
;
newfilename
:
string
;
};
}
src/app.ts
View file @
d1f8541e
...
...
@@ -12,6 +12,7 @@ app.use(cookieParser());
app
.
use
(
"
/api
"
,
router
);
app
.
use
(
"
/images
"
,
express
.
static
(
path
.
join
(
__dirname
,
"
..
"
,
"
/uploads
"
)));
app
.
use
(
"
/images
"
,
express
.
static
(
path
.
join
(
__dirname
,
"
..
"
,
"
/adminpics
"
)));
app
.
use
((
err
:
any
,
req
:
Request
,
res
:
Response
,
next
:
NextFunction
)
=>
{
console
.
log
(
"
익스프레스 에러:
"
,
err
);
...
...
src/controllers/mainimg.controller.ts
View file @
d1f8541e
...
...
@@ -4,35 +4,53 @@ import { TypedRequestAuth } from "./auth.controller";
import
{
asyncWrap
}
from
"
../helpers
"
;
import
{
mainimgDb
}
from
"
../db
"
;
import
{
TypedRequest
}
from
"
../types
"
;
import
{
ObjectId
}
from
"
mongoose
"
;
import
formidable
from
"
formidable
"
;
export
const
createMainimg
=
asyncWrap
(
async
(
reqExp
,
res
,
next
)
=>
{
const
req
=
reqExp
as
TypedRequestAuth
<
{
userId
:
string
}
>
;
export
const
createMainimg
=
asyncWrap
(
async
(
reqExp
,
res
)
=>
{
const
req
=
reqExp
as
TypedRequestAuth
<
{
userId
:
ObjectId
}
>
;
const
{
userId
}
=
req
.
auth
;
const
{
theme
,
city
,
url
,
title
}
=
req
.
body
as
{
theme
:
string
;
city
:
string
;
url
:
string
;
title
:
string
;
};
const
form
=
formidable
({
uploadDir
:
"
adminpics
"
,
keepExtensions
:
true
,
multiples
:
false
,
});
console
.
log
(
"
body
"
,
req
.
body
);
form
.
parse
(
req
,
(
err
,
fields
,
files
)
=>
{
if
(
!
Array
.
isArray
(
files
.
mainimg
))
{
//파일 좁히기 중
if
(
!
(
Array
.
isArray
(
fields
.
city
)
||
Array
.
isArray
(
fields
.
title
)
||
Array
.
isArray
(
fields
.
theme
)
)
)
{
const
city
=
fields
.
city
;
const
title
=
fields
.
title
;
const
theme
=
fields
.
theme
;
if
(
!
isLength
(
url
??
""
,
{
min
:
1
}))
{
return
res
.
status
(
422
).
send
(
"
이미지 url을 입력해주세요
"
);
// if (!isLength(title ?? "", { min: 1 })) {
// return res.status(422).send("이미지 제목을 입력해주세요");
// }
console
.
log
(
files
);
const
originalfilename
=
files
.
mainimg
?.
originalFilename
;
const
newfilename
=
files
.
mainimg
.
newFilename
;
if
(
!
(
originalfilename
===
null
||
newfilename
===
undefined
))
{
mainimgDb
.
createMainimg
(
{
city
,
title
,
theme
},
{
originalfilename
,
newfilename
,
}
);
}
}
if
(
!
isLength
(
title
??
""
,
{
min
:
1
}))
{
return
res
.
status
(
422
).
send
(
"
이미지 제목을 입력해주세요
"
);
}
const
newMainimg
=
await
mainimgDb
.
createMainimg
({
theme
,
city
,
url
,
title
,
});
re
turn
res
.
json
(
newMainimg
);
re
s
.
json
(
);
});
export
const
getMainimg
=
asyncWrap
(
async
(
req
,
res
)
=>
{
...
...
@@ -40,7 +58,6 @@ export const getMainimg = asyncWrap(async (req, res) => {
return
res
.
json
(
mainimgs
);
});
export
const
deleteMainimg
=
asyncWrap
(
async
(
req
,
res
)
=>
{
const
{
imgId
}
=
req
.
params
;
console
.
log
(
imgId
);
...
...
@@ -48,6 +65,3 @@ export const deleteMainimg = asyncWrap(async (req, res) => {
return
res
.
json
(
deleteCount
);
});
src/controllers/user.controller.ts
View file @
d1f8541e
import
{
userDb
}
from
"
../db
"
;
import
{
asyncWrap
}
from
"
../helpers/asyncWrap
"
;
import
{
Request
}
from
"
express
"
;
import
formidable
,
{
Fields
,
Files
}
from
"
formidable
"
;
import
formidable
from
"
formidable
"
;
import
{
ObjectId
}
from
"
mongoose
"
;
import
fs
from
"
fs
"
;
...
...
src/db/mainimg.db.ts
View file @
d1f8541e
import
{
Mainimg
,
MainimgType
}
from
"
../models
"
;
import
{
ObjectId
}
from
"
mongoose
"
;
import
{
Avatar
,
IAvatar
,
Mainimg
,
MainimgType
}
from
"
../models
"
;
export
const
createMainimg
=
async
(
mainimg
:
MainimgType
,
pic
:
IAvatar
)
=>
{
const
newPic
=
await
Avatar
.
create
({
originalfilename
:
pic
.
originalfilename
,
newfilename
:
pic
.
newfilename
,
pictureauth
:
pic
.
picturepath
,
});
export
const
createMainimg
=
async
(
mainimg
:
MainimgType
)
=>
{
const
newMainimg
=
await
Mainimg
.
create
({
theme
:
mainimg
.
theme
,
city
:
mainimg
.
city
,
url
:
mainimg
.
url
,
pic
:
newPic
.
_id
,
title
:
mainimg
.
title
,
});
return
newMainimg
;
};
export
const
getMainimg
=
async
()
=>
{
const
users
=
await
Mainimg
.
find
({});
return
users
;
};
const
img
=
await
Mainimg
.
find
({}).
populate
(
"
pic
"
);
return
img
;
};
export
const
deleteOneMainimg
=
async
(
_id
:
string
)
=>
{
const
res
=
await
Mainimg
.
deleteOne
({
_id
:
_id
});
...
...
src/db/user.db.ts
View file @
d1f8541e
import
bcrypt
from
"
bcryptjs
"
;
import
{
ObjectId
}
from
"
mongoose
"
;
import
{
IUser
,
Role
,
Post
,
User
,
Avatar
}
from
"
../models
"
;
import
fs
from
"
fs
"
;
import
fs
from
"
fs
/promises
"
;
export
const
createUser
=
async
(
user
:
IUser
)
=>
{
// 비밀번호 암호화
...
...
@@ -106,9 +106,7 @@ export const deleteUser = async (userId: ObjectId) => {
const
user
=
await
User
.
findById
(
userId
);
if
(
!
(
user
?.
avatar
===
undefined
))
{
const
ref
=
await
Avatar
.
findById
(
user
.
avatar
.
_id
);
fs
.
unlink
(
"
../travel/uploads/
"
+
ref
?.
newfilename
,
(
err
)
=>
{
console
.
log
(
err
);
});
await
fs
.
unlink
(
"
../travel/uploads/
"
+
ref
?.
newfilename
);
await
Avatar
.
deleteOne
({
_id
:
user
.
avatar
.
_id
});
await
User
.
deleteOne
({
_id
:
userId
});
}
...
...
src/models/mainimg.model.ts
View file @
d1f8541e
import
{
model
,
Schema
}
from
"
mongoose
"
;
import
{
model
,
Schema
,
Types
}
from
"
mongoose
"
;
export
interface
MainimgType
{
theme
:
string
;
city
:
string
;
url
:
string
;
title
:
string
;
pic
?:
Types
.
ObjectId
;
}
const
MainimgSchema
=
new
Schema
<
MainimgType
>
({
...
...
@@ -15,14 +14,11 @@ const MainimgSchema = new Schema<MainimgType>({
city
:
{
type
:
String
,
},
url
:
{
type
:
String
,
},
title
:
{
type
:
String
,
required
:
true
,
},
pic
:
{
type
:
Schema
.
Types
.
ObjectId
,
ref
:
"
Avatar
"
},
});
export
default
model
<
MainimgType
>
(
"
Mainimg
"
,
MainimgSchema
);
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment