Compare commits

...
Author SHA1 Message Date
Justin Lin 0e37b13d99 wip: not working yet... 2025-03-11 19:56:56 +08:00
Justin Lin b2a251576c fix: save stdout and stderr without tee 2025-02-22 14:07:15 +08:00
Justin Lin 8cc006e3a4 fix: check files using abs path (#175)
the docker user is changed from commander to root due to #168 pr.
Use absolute path instead of relative path to prevent false detection.
2025-02-15 20:55:03 +08:00
Justin Lin d6ae6d818d Fix network (#174) 2025-02-15 20:52:25 +08:00
wancat 6692ae2b9b fix: check files using abs path
(the docker user is changed due to feat-ssh pr)
2025-02-15 20:42:50 +08:00
Justin Lin fab02d03a9 fix: update env samples, use dash only for naming networks 2024-12-27 21:01:31 +08:00
Justin Lin 9e44ebe415 fix: remove unwanted error logging 2024-12-27 20:56:23 +08:00
Justin Lin 019caf58e4 fix: use a different docker network for dev to prevent "interna" settings conflicts 2024-12-27 20:56:05 +08:00
11 changed files with 70 additions and 45 deletions

No files matched your search

+1 -1
View File
@@ -3,6 +3,7 @@ PORT=3000
BASE_URL=https://example.com BASE_URL=https://example.com
SECRET_KEY=YOU_SHOULD_CHANGE_THIS SECRET_KEY=YOU_SHOULD_CHANGE_THIS
BACKEND_URL=http://backend:3000 BACKEND_URL=http://backend:3000
DOCKER_NETWORK=linux-odyssey-players
MONGO_ROOT_PASSWORD=adminpassword MONGO_ROOT_PASSWORD=adminpassword
MONGO_USER=commander MONGO_USER=commander
@@ -29,7 +30,6 @@ ADMIN_PORT=3001
# GITHUB_CLIENT_SECRET= # GITHUB_CLIENT_SECRET=
# SURVEY_URL= # SURVEY_URL=
# BUG_REPORT_URL= # BUG_REPORT_URL=
# DOCKER_NETWORK=
# SURVEY_URL= # SURVEY_URL=
# BUG_REPORT_URL= # BUG_REPORT_URL=
+1 -1
View File
@@ -3,6 +3,7 @@ PORT=3000
BASE_URL=http://localhost:5173 BASE_URL=http://localhost:5173
MONGO_URL=mongodb://localhost:27017/odyssey-test MONGO_URL=mongodb://localhost:27017/odyssey-test
SECRET_KEY=sample-secret SECRET_KEY=sample-secret
DOCKER_NETWORK=linux-odyssey-players-dev
# Optional # Optional
# BACKEND_URL=http://host.docker.internal:3000 # BACKEND_URL=http://host.docker.internal:3000
@@ -17,7 +18,6 @@ SECRET_KEY=sample-secret
# GITHUB_CLIENT_SECRET= # GITHUB_CLIENT_SECRET=
# SURVEY_URL= # SURVEY_URL=
# BUG_REPORT_URL= # BUG_REPORT_URL=
# DOCKER_NETWORK=
# SURVEY_URL= # SURVEY_URL=
# BUG_REPORT_URL= # BUG_REPORT_URL=
+3 -4
View File
@@ -13,7 +13,7 @@ services:
base: base:
image: linuxodyssey/quest-base image: linuxodyssey/quest-base
networks: networks:
- players - players-dev
build: build:
context: . context: .
dockerfile: ./quests/Dockerfile dockerfile: ./quests/Dockerfile
@@ -22,7 +22,6 @@ services:
networks: networks:
internal: internal:
players: players-dev:
name: linux-odyssey-players name: linux-odyssey-players-dev
internal: false internal: false
external: true
+29 -8
View File
@@ -1,24 +1,39 @@
PROMPT='%F{2}%n%f:%F{6}%~%f $ ' PROMPT='%F{2}%n%f:%F{6}%~%f $ '
unsetopt CASE_GLOB unsetopt CASE_GLOB
setopt CHASE_LINKS setopt CHASE_LINKS
emulate bash # emulate bash
set +o prompt_cr set +o prompt_cr
preexec() { preexec() {
export CMD_START_TIME=$(date +%s%N) # capture start time export CMD_START_TIME=$(date +%s%N) # capture start time
export CMD_NAME=$1 # capture command export CMD_NAME="$1" # capture command
export CMD_PID=$$
# A list of interactive commands # # A list of interactive commands
interactive_commands=("vim" "nano" "top" "ssh" "less" "zsh" "bash") # readlink /proc/$PPID/exe
interactive_commands=("vim" "nano" "top" "ssh" "less" "zsh" "bash" "script" "exec")
# # if [[ $CMD_EXECUTED = 1 ]]; then
# # export CMD_EXECUTED=0
# # return
# # fi
# Check if the command is in the list of interactive commands # # Check if the command is in the list of interactive commands
if [[ " ${interactive_commands[@]} " =~ " $1 " ]]; then if [[ " ${interactive_commands[@]} " =~ " $1 " ]]; then
# If it is, just execute it normally # If it is, just execute it normally
exec # exec
# echo IF
return
else else
# If it's not, execute it and redirect its output to a file # If it's not, execute it and redirect its output to a file
exec > >(tee /tmp/cmd.out) 2> >(tee /tmp/cmd.error >&2) # exec > >(tee /tmp/cmd.out) 2> >(tee /tmp/cmd.error >&2)
# echo ELSE
# export CMD_EXECUTED=1
# eval >&1 >/tmp/cmd.out
# exec >&1
# exec
script --quiet -O /tmp/cmd.out -c "$CMD_NAME"
return 1
fi fi
} }
@@ -29,7 +44,12 @@ precmd() {
export CMD_ERROR_FILE=/tmp/cmd.error export CMD_ERROR_FILE=/tmp/cmd.error
# Now run your Node.js script with the result as an argument # Now run your Node.js script with the result as an argument
node /usr/local/lib/container/cli.js; rm /tmp/cmd.out /tmp/cmd.error 2>/dev/null node /usr/local/lib/container/cli.js
if [[ -n $CMD_PID ]]; then
# echo $CMD_PID
# kill $CMD_PID
fi
# rm /tmp/cmd.out /tmp/cmd.error 2>/dev/null
# Reset variables # Reset variables
unset CMD_START_TIME unset CMD_START_TIME
@@ -38,5 +58,6 @@ precmd() {
unset CMD_OUTPUT_FILE unset CMD_OUTPUT_FILE
unset CMD_ERROR_FILE unset CMD_ERROR_FILE
unset CMD_NAME unset CMD_NAME
export CMD_EXECUTED=0
} }
+4 -4
View File
@@ -164,7 +164,7 @@ stages:
command: command:
- ^cp hello.txt hello2.txt$ - ^cp hello.txt hello2.txt$
files: files:
- path: hello2.txt - path: /home/commander/hello2.txt
type: file type: file
exceptions: exceptions:
- condition: - condition:
@@ -224,7 +224,7 @@ stages:
command: command:
- ^cp world.txt world2.txt$ - ^cp world.txt world2.txt$
files: files:
- path: world2.txt - path: /home/commander/world2.txt
type: file type: file
exceptions: exceptions:
- condition: - condition:
@@ -292,9 +292,9 @@ stages:
command: command:
- ^cp - ^cp
files: files:
- path: notes2 - path: /home/commander/notes2
type: directory type: directory
- path: notes2/note.txt - path: /home/commander/notes2/note.txt
type: file type: file
exceptions: exceptions:
- condition: - condition:
+6 -6
View File
@@ -44,12 +44,12 @@ stages:
- ls - ls
condition: condition:
files: files:
- path: hello.txt - path: /home/commander/hello.txt
type: file type: file
exceptions: exceptions:
- condition: - condition:
files: files:
- path: hello$ - path: /home/commander/hello
type: file type: file
responses: responses:
- type: narrative - type: narrative
@@ -107,12 +107,12 @@ stages:
- ls-touch - ls-touch
condition: condition:
files: files:
- path: world.txt - path: /home/commander/world.txt
type: file type: file
exceptions: exceptions:
- condition: - condition:
files: files:
- path: world$ - path: /home/commander/world
type: file type: file
responses: responses:
- type: narrative - type: narrative
@@ -154,7 +154,7 @@ stages:
- ls-2 - ls-2
condition: condition:
files: files:
- path: notes - path: /home/commander/notes
type: folder type: folder
responses: responses:
- type: narrative - type: narrative
@@ -203,7 +203,7 @@ stages:
- cd-notes - cd-notes
condition: condition:
files: files:
- path: notes/note.txt - path: /home/commander/notes/note.txt
type: file type: file
responses: responses:
- type: narrative - type: narrative
+8 -8
View File
@@ -49,9 +49,9 @@ stages:
command: command:
- ^mv hello.txt notes$ - ^mv hello.txt notes$
files: files:
- path: notes/hello.txt - path: /home/commander/notes/hello.txt
type: file type: file
- path: hello.txt - path: /home/commander/hello.txt
exists: false exists: false
responses: responses:
- type: narrative - type: narrative
@@ -106,9 +106,9 @@ stages:
command: command:
- ^mv - ^mv
files: files:
- path: wow.txt - path: /home/commander/wow.txt
type: file type: file
- path: world.txt - path: /home/commander/world.txt
exists: false exists: false
responses: responses:
- type: narrative - type: narrative
@@ -148,9 +148,9 @@ stages:
command: command:
- ^mv - ^mv
files: files:
- path: docs - path: /home/commander/docs
type: directory type: directory
- path: notes - path: /home/commander/notes
exists: false exists: false
responses: responses:
- type: narrative - type: narrative
@@ -187,9 +187,9 @@ stages:
command: command:
- ^mv - ^mv
files: files:
- path: hi.txt - path: /home/commander/hi.txt
type: file type: file
- path: docs/hello.txt - path: /home/commander/docs/hello.txt
exists: false exists: false
exceptions: exceptions:
- condition: - condition:
+10 -10
View File
@@ -44,7 +44,7 @@ stages:
command: command:
- ^rm\s - ^rm\s
files: files:
- path: hello.txt - path: /home/commander/hello.txt
exists: false exists: false
responses: responses:
- content: | - content: |
@@ -58,7 +58,7 @@ stages:
command: command:
- ^ls$ - ^ls$
files: files:
- path: hello.txt - path: /home/commander/hello.txt
exists: false exists: false
responses: responses:
- content: | - content: |
@@ -78,9 +78,9 @@ stages:
command: command:
- ^rm - ^rm
files: files:
- path: world.txt - path: /home/commander/world.txt
exists: false exists: false
- path: wow.txt - path: /home/commander/wow.txt
exists: false exists: false
responses: responses:
- content: | - content: |
@@ -111,7 +111,7 @@ stages:
command: command:
- ^rmdir empty$ - ^rmdir empty$
files: files:
- path: empty - path: /home/commander/empty
exists: false exists: false
responses: responses:
- content: | - content: |
@@ -139,7 +139,7 @@ stages:
command: command:
- ^rmdir notes$ - ^rmdir notes$
files: files:
- path: notes - path: /home/commander/notes
type: folder type: folder
exists: true exists: true
responses: responses:
@@ -161,10 +161,10 @@ stages:
command: command:
- ^rm - ^rm
files: files:
- path: notes - path: /home/commander/notes
type: folder type: folder
exists: true exists: true
- path: notes/note.txt - path: /home/commander/notes/note.txt
exists: false exists: false
exceptions: exceptions:
- condition: - condition:
@@ -203,7 +203,7 @@ stages:
task: 使用 `rmdir` 刪除 `notes` 資料夾 task: 使用 `rmdir` 刪除 `notes` 資料夾
condition: condition:
files: files:
- path: notes - path: /home/commander/notes
exists: false exists: false
responses: responses:
- content: | - content: |
@@ -238,7 +238,7 @@ stages:
command: command:
- ^rm -r trash$ - ^rm -r trash$
files: files:
- path: trash - path: /home/commander/trash
exists: false exists: false
responses: responses:
- content: | - content: |
@@ -8,6 +8,7 @@ import { asyncHandler } from '../../middleware/error.js'
export const newCommand = asyncHandler(async (req: Request, res: Response) => { export const newCommand = asyncHandler(async (req: Request, res: Response) => {
const { command, pwd, output, error, params } = matchedData(req) const { command, pwd, output, error, params } = matchedData(req)
console.log(command, output, error)
const { sessionId } = req.user as any const { sessionId } = req.user as any
const session = await Session.findById(sessionId) const session = await Session.findById(sessionId)
+1 -1
View File
@@ -72,7 +72,7 @@ function createConfig() {
projectRoot, projectRoot,
docker: { docker: {
network: get('DOCKER_NETWORK', 'linux-odyssey-players'), network: get('DOCKER_NETWORK', 'linux-odyssey-players-dev'),
defaultImage: get('QUEST_IMAGE', 'linuxodyssey/quest-base'), defaultImage: get('QUEST_IMAGE', 'linuxodyssey/quest-base'),
imagePrefix: get('DOCKER_PREFIX', 'linuxodyssey/quest-'), imagePrefix: get('DOCKER_PREFIX', 'linuxodyssey/quest-'),
mountQuest: process.env.MOUNT_QUEST === 'true', mountQuest: process.env.MOUNT_QUEST === 'true',
+6 -2
View File
@@ -33,6 +33,9 @@ export async function createContainer(
imageId: string imageId: string
): Promise<Docker.Container> { ): Promise<Docker.Container> {
let binds: string[] = [] let binds: string[] = []
binds.push(
`${config.docker.hostProjectRoot}/quests/.includes/zshrc:/etc/zsh/zshrc`
)
if (await questHomeExists(questId)) { if (await questHomeExists(questId)) {
logger.info('Mounting quest home', questId) logger.info('Mounting quest home', questId)
binds.push( binds.push(
@@ -94,20 +97,21 @@ export async function attachContainer(
config.docker.network config.docker.network
].IPAddress ].IPAddress
let error: Error | null = null
for (let i = 0; i < 10; i++) { for (let i = 0; i < 10; i++) {
try { try {
// eslint-disable-next-line no-await-in-loop // eslint-disable-next-line no-await-in-loop
const stream = await connectToSSH(containerIp, { token }) const stream = await connectToSSH(containerIp, { token })
return stream return stream
} catch (err) { } catch (err) {
logger.error('Failed to connect to SSH', err) error = err as Error
// eslint-disable-next-line no-await-in-loop // eslint-disable-next-line no-await-in-loop
await new Promise((resolve) => { await new Promise((resolve) => {
setTimeout(resolve, 1000) setTimeout(resolve, 1000)
}) })
} }
} }
throw new Error('Failed to connect to SSH') throw new Error('Failed to connect to SSH', { cause: error })
} }
export async function deleteContainer(id: string) { export async function deleteContainer(id: string) {