PS C:\Users\gabriel> wsl --list --online
다음은 설치할 수 있는 유효한 배포판 목록입니다.
'wsl.exe --install <Distro>'를 사용하여 설치합니다.
NAME FRIENDLY NAME
Ubuntu Ubuntu
Debian Debian GNU/Linux
kali-linux Kali Linux Rolling
Ubuntu-18.04 Ubuntu 18.04 LTS
Ubuntu-20.04 Ubuntu 20.04 LTS
Ubuntu-22.04 Ubuntu 22.04 LTS
OracleLinux_7_9 Oracle Linux 7.9
OracleLinux_8_7 Oracle Linux 8.7
OracleLinux_9_1 Oracle Linux 9.1
SUSE-Linux-Enterprise-Server-15-SP4 SUSE Linux Enterprise Server 15 SP4
openSUSE-Leap-15.4 openSUSE Leap 15.4
openSUSE-Tumbleweed openSUSE Tumbleweed
PS C:\Users\gabriel>
특정 배포판 설치
wsl --install -d Ubuntu-20.04
설치된 Linux 확인
PS C:\Users\gabriel> wsl -l -v
NAME STATE VERSION
* Ubuntu Stopped 2
Ubuntu-20.04 Stopped 1
WSL 2로 전환
PS C:\Users\gabriel> wsl --set-version Ubuntu-20.04 2
WSL 2와의 주요 차이점에 대한 자세한 내용은 https://aka.ms/wsl2를 참조하세요
변환이 진행 중입니다. 이 작업은 몇 분 정도 걸릴 수 있습니다.
작업을 완료했습니다.
PS C:\Users\gabriel> wsl -l -v
NAME STATE VERSION
* Ubuntu Stopped 2
Ubuntu-20.04 Stopped 2
PS C:\Users\gabriel>
특정 배포를 기본으로 사용하고자 할경우
wsl --set-default <배포판이름> or wsl -s <배포판이름>
PS C:\Users\gabriel> wsl --set-default Ubuntu-20.04
작업을 완료했습니다.
PS C:\Users\gabriel> wsl -l -v
NAME STATE VERSION
* Ubuntu-20.04 Stopped 2
Ubuntu Stopped 2
PS C:\Users\gabriel>
WSL 업데이트
PS C:\Users\gabriel> wsl --update
설치 중: Linux용 Windows 하위 시스템
Linux용 Windows 하위 시스템이(가) 설치되었습니다.
PS C:\Users\gabriel>
WSL 상태확인
PS C:\Users\gabriel> wsl --status
기본 배포: Ubuntu
기본 버전: 2
PS C:\Users\gabriel>
PS C:\Users\gabriel> wsl --unregister Ubuntu-20.04
등록 취소 중입니다.
작업을 완료했습니다.
PS C:\Users\gabriel> wsl -l -v
NAME STATE VERSION
* Ubuntu Stopped 2
PS C:\Users\gabriel>
PS C:\Users\gabriel> dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
배포 이미지 서비스 및 관리 도구
버전: 10.0.19041.844
이미지 버전: 10.0.19045.2728
기능을 사용하도록 설정하는 중
[==========================100.0%==========================]
작업을 완료했습니다.
PS C:\Users\gabriel>
PS C:\Users\gabriel> dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart
배포 이미지 서비스 및 관리 도구
버전: 10.0.19041.844
이미지 버전: 10.0.19045.2728
기능을 사용하도록 설정하는 중
[==========================100.0%==========================]
작업을 완료했습니다.
PS C:\Users\gabriel>
wsl --set-default-version 2
PS C:\Users\gabriel> wsl --set-default-version 2
WSL 2와의 주요 차이점에 대한 자세한 내용은 https://aka.ms/wsl2를 참조하세요
작업을 완료했습니다.
PS C:\Users\gabriel>
wsl 버전확인
PS C:\Users\gabriel> wsl --update
업데이트 확인 중입니다.
Linux용 Windows 하위 시스템 최신 버전이 이미 설치되어 있습니다.
PS C:\Users\gabriel> wsl --version
WSL 버전: 1.2.5.0
커널 버전: 5.15.90.1
WSLg 버전: 1.0.51
MSRDC 버전: 1.2.3770
Direct3D 버전: 1.608.2-61064218
DXCore 버전: 10.0.25131.1002-220531-1700.rs-onecore-base2-hyp
Windows 버전: 10.0.19045.2728
PS C:\Users\gabriel>
Ubuntu 최신 배포판 설치
Windows 10 버전 2004 이상(빌드 19041 이상) 또는 Windows 11 인경우 아래 명령으로 기본 Ubuntu 최신 (22.02.4) 설치가능
wsl --install
만일 해당 명령으로 설치가 안될경우 아래 두가지 방법으로 수동으로 설치합니다.
첫번째 Microsoft Store에서 ubuntu 설치 (해당 옵션으로 설치했음)
"열기"를 누르면 자동으로 설치 prompt가 실행되며, 사용자 id, passwd를 입력합니다.
설치가 완료되면 exit
관리자 권한으로 터미널 실행
두번째, wsl install을 이용해서 Ubuntu 설치
설치가능한 목록 확인
PS C:\Users\gabriel> wsl --list --online
다음은 설치할 수 있는 유효한 배포 목록입니다.
'wsl --install -d <배포>'를 사용하여 설치하세요.
NAME FRIENDLY NAME
Ubuntu Ubuntu
Debian Debian GNU/Linux
kali-linux Kali Linux Rolling
Ubuntu-18.04 Ubuntu 18.04 LTS
Ubuntu-20.04 Ubuntu 20.04 LTS
Ubuntu-22.04 Ubuntu 22.04 LTS
OracleLinux_7_9 Oracle Linux 7.9
OracleLinux_8_7 Oracle Linux 8.7
OracleLinux_9_1 Oracle Linux 9.1
SUSE-Linux-Enterprise-Server-15-SP4 SUSE Linux Enterprise Server 15 SP4
openSUSE-Leap-15.4 openSUSE Leap 15.4
openSUSE-Tumbleweed openSUSE Tumbleweed
PS C:\Users\gabriel>
wsl --install -d Ubuntu
PS C:\Users\gabriel> wsl --install -d Ubuntu
ubuntu 설치 prompt가 자동으로 실행, 이때 명시적으로 Ubuntu 버전을 지정하지 않으면 "Ubuntu 22.04 LTS" 가 설치됨.
아이디 패스워드 입력
설치된 ubuntu 확인
앞단계 WSL 설치후에 "wsl --set-default-version 2" 로 버전을 지정해준후 ubuntu를 설치했기 때문에 VERSION 2로 설치되었지만, 만일 VERSION이 1 이라면 아래 명령으로 2로 바꿔 주도록 합니다.
wsl --set-version Ubuntu 2
새로 터미널을 열고 선택박스에서 Ubuntu 를 선택하여 실행 합니다.
설치된 ubuntu의 버전 확인 : cat /etc/lsb-release
설치후 Ubuntu 업데이트 및 업그레이드
gabriel@NB-15052600:~$ sudo apt update && sudo apt upgrade
[sudo] password for gabriel: <패스워드 입력>
Hit:1 http://archive.ubuntu.com/ubuntu jammy InRelease
Get:2 http://security.ubuntu.com/ubuntu jammy-security InRelease [110 kB]
Get:3 http://archive.ubuntu.com/ubuntu jammy-updates InRelease [119 kB]
Get:4 http://security.ubuntu.com/ubuntu jammy-security/main amd64 Packages [363 kB]
Get:5 http://archive.ubuntu.com/ubuntu jammy-backports InRelease [108 kB]
Get:6 http://security.ubuntu.com/ubuntu jammy-security/main Translation-en [108 kB]
Get:7 http://security.ubuntu.com/ubuntu jammy-security/main amd64 c-n-f Metadata [9732 B]
Get:8 http://security.ubuntu.com/ubuntu jammy-security/restricted amd64 Packages [225 kB]
Get:9 http://archive.ubuntu.com/ubuntu jammy/universe amd64 Packages [14.1 MB]
생략....
Preparing to unpack .../libkrb5-3_1.19.2-2ubuntu0.2_amd64.deb ...
Unpacking libkrb5-3:amd64 (1.19.2-2ubuntu0.2) over (1.19.2-2ubuntu0.1) ...
Setting up libkrb5-3:amd64 (1.19.2-2ubuntu0.2) ...
(Reading database ... 24137 files and directories currently installed.)
Preparing to unpack .../libgssapi-krb5-2_1.19.2-2ubuntu0.2_amd64.deb ...
Unpacking libgssapi-krb5-2:amd64 (1.19.2-2ubuntu0.2) over (1.19.2-2ubuntu0.1) ...
Setting up libgssapi-krb5-2:amd64 (1.19.2-2ubuntu0.2) ...
(Reading database ... 24137 files and directories currently installed.)
Preparing to unpack .../distro-info-data_0.52ubuntu0.4_all.deb ...
Unpacking distro-info-data (0.52ubuntu0.4) over (0.52ubuntu0.3) ...
Setting up distro-info-data (0.52ubuntu0.4) ...
Processing triggers for libc-bin (2.35-0ubuntu3.1) ...
gabriel@NB-15052600:~$
로그인 후 타임아웃시간을 지정해서 사용자가 일정시간동안 액션이 없다면 로그아웃시키는 기능을 추가합니다
별도의 세션을 체크하는 방법을 찾지 못해서 State에 login check var를 추가하고 로그인할때, 매뉴클릭시 각 화면의 권한 체크하는 pc.cond 로 login check var를 호출하도록 구현하도록 하겠습니다.
demo_state.py
날짜 변환 로컬 함수를 먼저 정의합니다.
from datetime import date, datetime, timezone, timedelta
# logincheck에서 사용할 함수 현재 시간을 리턴
def get_ymd_hms():
# 클라우드 서버가 싱가폴 데이터 센터에 있기때문에 UTC=8로 한국시간과 맞춘다.
servertime = timezone(timedelta(hours=8))
now = datetime.now(servertime)
now_ymd = now.strftime('%Y%m%d')
now_ymd_hms = now.strftime('%Y-%m-%d %H:%M:%S')
#print("now_ymd_hms : {}".format(now_ymd_hms))
return now_ymd_hms
# 문자열의 시간을 시간타입으로 변환
def get_strptime(ymd_hms):
datetime_format = '%Y-%m-%d %H:%M:%S'
datetime_ymd_hms = datetime.strptime(ymd_hms, datetime_format)
return datetime_ymd_hms
logincheck var 추가
class State(pc.State):
"""The base state for the app."""
username: str
logged_in: bool = False # 로그인 상태 True, False
authrole: str
logged_ymd_hms:str #로그인 및 마지막 액션 시간을 저장
@pc.var
def logincheck(self) -> bool:
"""Logged in check a user."""
# 현재 시간을 '%Y-%m-%d %H:%M:%S' 포켓으로 가져오기
now_ymd_hms = get_ymd_hms()
# 현재 로그인 상태이면
if self.logged_in:
# 로그인 상태이고, 로그인 시간이 기록되어 있으면
if len(self.logged_ymd_hms) > 0:
# 이전 로그인 또는 액션의 시간을 조회
before_time = get_strptime(self.logged_ymd_hms)
now_time = get_strptime(now_ymd_hms)
# 현재 시간과 이전 시간의 차이를 계산
cul_time = now_time - before_time
# 차이가 지정된 시간보다 크다면 로그인 상태 False로
if cul_time.seconds > 3600: #600 : 10 Minutes
self.logged_in = False
self.logged_ymd_hms = ""
return self.logged_in
# 지정된 시간안에 있으면, 현재 시간을 logged_ymd_hms에 저장
self.logged_ymd_hms = now_ymd_hms
return self.logged_in
demo_servers.py
demo_servers.py 의 servers 함수에서 pc.cond에서 로그인 상태 체크를 logged_in 에서 logincheck var 로 변경한다.
import pynecone as pc
from .demo_state import State # Substate of Base State
from .demo_helpers import navbar
class ServerState(State):
"""The Substate of base state for the server page."""
print("──────────────────── [ ServerState(State) ] ────────────────────")
app_name: str = "Servers" #1 Navigation Bar에 표시될 Page 명
def servers():
return pc.box(
pc.vstack(
navbar(State, ServerState.app_name), #2 맨위 상단에 navbar를 추가
pc.cond(
State.logged_in,
pc.box(
pc.vstack(
pc.heading("서버 모니터링 페이지", font_size="2em"),
),
width="100%",
border_width="0px",
),
pc.link(
pc.button("먼저 로그인 하세요"),
href="/login",
color="rgb(107,99,246)",
button=True,
)
),
padding_top="5em",
width="100%",
),
)
demo_server.py 화면을 구성하기위한 데이터는 따로 python 서비스 를 구현해서 데이터를 List[Dict] Type으로 리턴 받는다는 가정하에 샘플데이터를 만들어서 구현하겠습니다.
추가되는 함수 설명
defget_data() # Table, DataTable을 위한 Sample List[Dict] 데이터 리턴
logout 이 호출되면 State에 저장된값을 모두 reset 시키고 "/login" 페이지로 redirect.
import pynecone as pc
class User(pc.Model, table=True):
"""A table of Users."""
username: str
password: str
userrole: str
class State(pc.State):
"""The base state for the app."""
print("──────────────────── [ State(pc.State) ] ────────────────────")
username: str
userrole: str
logged_in: bool = False
def logout(self):
"""Log out a user."""
self.reset()
return pc.redirect("/login")
import pynecone as pc
#table=True 인수는 Pynecone에게 이 클래스에 대해 데이터베이스에 테이블을 생성하도록 지시
class User(pc.Model, table=True):
"""A table of Users."""
username: str # 사용자명
password: str # 패스워드
userrole: str # 사용자 권한
class State(pc.State):
"""The base state for the app."""
print("──────────────────── [ State(pc.State) ] ────────────────────")
username: str
userrole: str
logged_in: bool = False # 로그인/로그아웃시 상태를 저장
demo_auth.py 수정
Log in / Sign up 을위해서 추가 변경되는 부분이 많습니다.
import pynecone as pc
from .demo_state import State , User # Base State, User Class import
from .demo_helpers import navbar
# 로그인 호면에 대한 스타일 시트 적용
styles = {
"login_page": {
"padding_top": "10em",
"text_align": "top",
"position": "relative",
"background_image": "bg.svg",
"background_size": "100% auto",
"width": "100%",
"height": "100vh",
},
"login_input": {
"shadow": "lg",
"padding": "1em",
"border_radius": "lg",
"background": "white",
},
}
class AuthState(State):
"""The Substate of base state for the login page."""
print("──────────────────── [ AuthState(State) ] ────────────────────")
app_name: str = "Log In"
password: str # 로그인에서 입력받은 Password
new_username: str # 신규 사용자 Name
new_userrole: str # 신규 사용자 Role
new_password: str # 신규 사용자 Password
confirm_password: str # 신규 사용자 Confirm Password
message: str # 사용자 등록시 발생하는 Error 등 메시지 표시
message_color: str = "red" # Message의 기본색은 red, Sign up 성공인경우 navy
def signup(self):
"""Sign up a user."""
print("signup values : [{}], [{}], [{}]".format(self.new_username, self.new_password, self.new_userrole))
with pc.session() as session:
#self.message_color = "red"
# 입력 값 Validation 체크
if self.new_userrole == "" or self.new_userrole == None:
self.message = "Select user role."
return
if self.new_username == "" or self.new_username == None:
self.message = "Input user name."
return
if self.new_password == "" or self.new_password == None:
self.message = "Input passwd"
return
if self.new_password != self.confirm_password:
self.message = "Passwords do not match."
return
if session.exec(User.select.where(User.username == self.new_username)).first():
self.message = "Username already exists."
return
user = User(username=self.new_username, password=self.new_password, userrole=self.new_userrole)
session.add(user)
session.commit()
self.logged_in = False
self.message_color = "navy"
self.message = "User siginup completed, please log in"
return pc.redirect("/login")
def login(self):
"""Log in a user."""
if self.username == "" or self.password=="":
self.message = "Input username and password."
return
with pc.session() as session:
user = session.exec(
User.select.where(User.username == self.username)
).first()
if user and user.password == self.password:
self.logged_in = True
self.userrole = user.userrole
return pc.redirect("/servers")
else:
self.message = "Invalid username or password."
return
# Signup 화면에서 선택한 Role 정보를 AuthState에 저장
def role_change(self,value):
print("role_change value : {}".format(value))
self.new_userrole = value
def clear_message(self):
self.message = ""
self.message_color = "red"
def signup():
return pc.box(
pc.vstack(
navbar(State, AuthState.app_name),
pc.center(
pc.vstack(
pc.heading("Sign Up", font_size="1.5em"),
pc.select(
["User", "Admin"],
placeholder="Select a Role.",
on_change=lambda value: AuthState.role_change(value),
border_style="solid",
border_width="1px",
border_color="#43464B",
),
pc.input(
on_blur=AuthState.set_new_username, placeholder="Username", width="100%",
on_click=AuthState.clear_message,
),
pc.input(
on_blur=AuthState.set_new_password,
placeholder="Password",
type_="password",
width="100%",
on_click=AuthState.clear_message,
),
pc.input(
on_blur=AuthState.set_confirm_password,
placeholder="Confirm Password",
type_="password",
width="100%",
on_click=AuthState.clear_message,
),
pc.button("Sign Up", on_click=AuthState.signup, width="100%"),
pc.text(AuthState.message, color=AuthState.message_color),
),
style=styles["login_input"],
),
),
style=styles["login_page"],
)
def login():
return pc.box(
pc.vstack(
navbar(State, AuthState.app_name),
pc.center(
pc.vstack(
pc.heading("Log In", font_size="1.5em"),
pc.input(
on_blur=State.set_username, placeholder="Username", width="100%",
on_click=AuthState.clear_message,
),
pc.input(
on_change=AuthState.set_password,
placeholder="Password",
type_="password",
width="100%",
on_click=AuthState.clear_message,
),
# Username, Password를 입력받으면 AuthState의 login 함수 호출하는 event 작동
pc.button("Login", on_click=AuthState.login, width="100%"),
# 사용자 등록하기위한 Signup
pc.link(
pc.button("Sign Up", width="100%"), href="/signup", width="100%"
),
pc.text(AuthState.message, color=AuthState.message_color),
),
style=styles["login_input"], # 로그인 Page에 맞는 추가 스타일
),
),
style=styles["login_page"],
)
import pynecone as pc
from .demo_state import State # Substate of Base State
from .demo_helpers import navbar
class ServerState(State):
"""The Substate of base state for the server page."""
print("──────────────────── [ ServerState(State) ] ────────────────────")
app_name: str = "Servers" #1 Navigation Bar에 표시될 Page 명
def servers():
return pc.box(
pc.vstack(
navbar(State, ServerState.app_name), #2 맨위 상단에 navbar를 추가
pc.cond(
State.logged_in,
pc.box(
pc.vstack(
pc.heading("서버 모니터링 페이지", font_size="2em"),
),
width="100%",
border_width="0px",
),
pc.link(
pc.button("먼저 로그인 하세요"),
href="/login",
color="rgb(107,99,246)",
button=True,
)
),
padding_top="5em",
width="100%",
),
)
로그인 없이 접근할경우
demo_board.py
"""Welcome to Pynecone! This file outlines the steps to create a basic app."""
from pcconfig import config
import pynecone as pc
from .demo_state import State
from .demo_auth import login, signup # Page 추가
from .demo_servers import servers
def index():
return pc.center(
pc.vstack(
pc.heading("Pynecone Demo Board!", font_size="2em"),
pc.link(
"로그인",
href="/login",
border="0.1em solid",
padding="0.5em",
border_radius="0.5em",
_hover={
"color": "rgb(107,99,246)",
},
),
spacing="1.5em",
font_size="2em",
),
padding_top="10%",
)
# Add state and page to the app.
app = pc.App(state=State)
app.add_page(index)
app.add_page(login)
app.add_page(signup) # Page 추가
app.add_page(servers) # Page 추가
app.compile()
서버 재시작 (ctrl + c & pc run)
서버 재구동후 DB Browser for SQLite 로 pynecone.db 파일을 열어보면 user 테이블이 생성된것을 확인
import pynecone as pc
class State(pc.State):
"""The base state for the app."""
print("──────────────────── [ State(pc.State) ] ────────────────────")
#4 demo_auth.py 생성
import pynecone as pc
from .demo_state import State # Substate of Base State
class AuthState(State):
"""The Substate of base state for the login page."""
print("──────────────────── [ AuthState(State) ] ────────────────────")
def login():
return pc.box(
pc.vstack(
pc.heading("로그인 페이지", font_size="2em"),
padding_top="5em",
width="100%",
),
)
#2 demo_board.py 수정
- State(pc.State) 클래스 제거,
- 앞에서 생성한 demo_state.py의 Base State import
- link를 추가한 demo_auth.py의 /login Page로 변경
"""Welcome to Pynecone! This file outlines the steps to create a basic app."""
from pcconfig import config
import pynecone as pc
from .demo_state import State #1 새로 생성한 demo_state.py page의 State import
from .demo_auth import login #2 새로 생성한 login page를 import
def index():
return pc.center(
pc.vstack(
pc.heading("Pynecone Demo Board!", font_size="2em"),
pc.link(
"로그인", #3 화면 내용 수정
href="/login",
border="0.1em solid",
padding="0.5em",
border_radius="0.5em",
_hover={
"color": "rgb(107,99,246)",
},
),
spacing="1.5em",
font_size="2em",
),
padding_top="10%",
)
# Add state and page to the app.
app = pc.App(state=State)
app.add_page(index)
app.add_page(login) #4 로그인 페이지를 App에 등록
app.compile()
실행 - pc run
#5 demo_helpers.py 추가
로그인 Page 및 추가될 Page들에서 공통으로 사용할 Navigation Bar를 화면 상단에 fix 크기로 생성합니다.
import pynecone as pc
from .demo_state import State # Base State의 Vars들을 사용하기위해서 import
def navbar(State, app_name): #1 Nav.Bar 상단에 각 Page의 이름을 표시하기위해서 파라미터로 받음.
"""The navbar."""
return pc.box(
pc.hstack(
pc.hstack(
pc.image(src="bada.png", width="48px"), #2 회사 로고가있어서 좌측상단에 표시
pc.heading("Demo Board") #3 로고 옆에 프로젝트 이름
pc.heading(" - "+app_name, size="lg", color="navy"), #4 Page 이름 표시
),
pc.spacer(),
justify="space-between",
border_bottom="0.15em solid #d3d3d3",
padding_x="2em",
padding_y="1em",
bg="rgba(255,255,255, 1)",
),
position="fixed", #5 Box의 위치 및 크기를 지정후 고정한다.
width="100%",
top="0px",
z_index="500",
)
생성한 Navigation Bar를 login Page에 붙여 넣기
- login page 의 이름을 지정하고
- navbar를 추가할때 page 이름을 파라미터로 전달
import pynecone as pc
from .demo_state import State # Substate of Base State
class AuthState(State):
"""The Substate of base state for the login page."""
print("──────────────────── [ AuthState(State) ] ────────────────────")
app_name: str = "Log In" #1 Navigation Bar에 표시될 Page 명
def login():
return pc.box(
pc.vstack(
navbar(State, AuthState.app_name), #2 맨위 상단에 navbar를 추가
pc.heading("로그인 페이지", font_size="2em"),
padding_top="5em",
width="100%",
),
)