Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create separate docker for each feature branch #468

Merged
merged 5 commits into from
Jul 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added .github/pull_request.md
Empty file.
1 change: 1 addition & 0 deletions .github/workflows/DevelopServerDeploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ jobs:
run: |
docker run -d --name wikigdrive-develop \
--restart unless-stopped \
--network nginx \
-v wikiGDriveDevelop:/data \
-v /home/wikigdrive/service_account.json:/service_account.json \
-v /home/wikigdrive/env.develop:/usr/src/app/.env \
Expand Down
45 changes: 45 additions & 0 deletions .github/workflows/feat-deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: Feat Branch Deploy

on:
push:
branches:
- feat/*

jobs:
test:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- run: sudo apt-get update
- run: sudo apt-get install -y libkrb5-dev

- name: Use Node.js 20.x
uses: actions/setup-node@v3
with:
node-version: 20
cache: npm

- name: Install node_modules
run: npm install

- name: Lint
run: npm run lint

- name: Test
run: npm run test

build:
needs: test
runs-on: wgd-dev

steps:
- name: Create pull request
id: open-pr
uses: repo-sync/pull-request@v2
with:
destination_branch: "master"
pr_title: "${BRANCH_NAME}"
pr_template: ".github/pull_request.md"
pr_draft: true
105 changes: 67 additions & 38 deletions .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ name: Pull request created
on:
pull_request:
branches: [ master ]
types: [edited, synchronize]

jobs:
build:
test:
runs-on: ubuntu-latest

steps:
Expand All @@ -29,43 +30,71 @@ jobs:
- name: Test
run: npm run test

test:
build:
needs: test
runs-on: wgd-dev

steps:
- uses: actions/checkout@v3

- name: Use Node.js 20.x
uses: actions/setup-node@v3
with:
node-version: 20
cache: npm

- name: Build action runner
run: docker build -t "wgd-action-runner:test" --build-arg "GIT_SHA=${GITHUB_SHA}" apps/wgd-action-runner

- name: Build hugo docs
run: |
docker run \
-v "${GITHUB_WORKSPACE}/hugo:/site" \
-v "${GITHUB_WORKSPACE}/website:/website" \
-v "/var/www/test.wikigdrive.com:/dist/hugo" \
--env CONFIG_TOML="/site/config/_default/config.toml" --env BASE_URL="https://test.wikigdrive.com" \
wgd-action-runner:test /steps/step_render_hugo

- name: Copy index for vite
run: mkdir -p dist/hugo && cp -rf /var/www/test.wikigdrive.com/* dist/hugo

- name: Build
run: docker build -t wikigdrive-test --build-arg "GIT_SHA=${GITHUB_SHA}" .

- name: Stop
run: docker stop wikigdrive-test
continue-on-error: true

- name: Remove
run: docker rm wikigdrive-test
continue-on-error: true

- name: Start
run: docker run -t -v wikiGDriveExample:/data -v /home/wikigdrive/service_account.json:/service_account.json -v "/var/www/dev.wikigdrive.com:/usr/src/app/dist/hugo" wikigdrive-test wikigdrive --service_account /service_account.json --share_email [email protected] --transform_subdir / --workdir /data pull 0AIkOKXbzWCtSUk9PVA
- name: Test
run: echo "${{ github.event.number }}"

- uses: actions/checkout@v3

- name: Use Node.js 20.x
uses: actions/setup-node@v3
with:
node-version: 20
cache: npm

- name: Build action runner
run: docker build -t "wgd-action-runner:pr-${{ github.event.number }}" --build-arg "GIT_SHA=${{ github.sha }}" apps/wgd-action-runner

- name: Build hugo docs
run: |
docker run \
-v "${GITHUB_WORKSPACE}/hugo:/site" \
-v "${GITHUB_WORKSPACE}/website:/website" \
-v "/var/www/pr-${{ github.event.number }}.wikigdrive.com:/dist/hugo" \
--env CONFIG_TOML="/site/config/_default/config.toml" --env BASE_URL="https://pr-${{ github.event.number }}.wikigdrive.com" \
wgd-action-runner:pr-${{ github.event.number }} /steps/step_render_hugo

- name: Copy index for vite
run: mkdir -p ${GITHUB_WORKSPACE}/dist/hugo && cp -rf /var/www/pr-${{ github.event.number }}.wikigdrive.com/* ${GITHUB_WORKSPACE}/dist/hugo

- name: build
uses: whoan/docker-build-with-cache-action@v5
with:
image_name: "wikigdrive-feature"
image_tag: "${{ github.sha }}"
push_image_and_stages: false
build_extra_args: "{'--build-arg': 'GIT_SHA=${{ github.sha }}'}"

- name: Stop and remove
run: docker stop "pr-${{ github.event.number }}" ; docker rm "pr-${{ github.event.number }}"
continue-on-error: true

- name: "Create empty volume"
run: docker volume rm -f "pr-${{ github.event.number }}" ; docker volume create "pr-${{ github.event.number }}"

- name: Start
run: |
docker run -d --name "pr-${{ github.event.number }}" \
--restart unless-stopped \
--network nginx \
-v "pr-${{ github.event.number }}":/data \
-v /home/wikigdrive/service_account.json:/service_account.json \
-v /home/wikigdrive/env.develop:/usr/src/app/.env \
-v /var/run/docker.sock:/var/run/docker.sock \
-v "/var/www/pr-${{ github.event.number }}.wikigdrive.com:/usr/src/app/dist/hugo" \
-e "GIT_SHA=${{ github.sha }}" \
-e "ZIPKIN_URL=https://pr-${{ github.event.number }}.wikigdrive.com/zipkin" \
-e "ZIPKIN_SERVICE=pr-${{ github.event.number }}" \
-e "AUTH_DOMAIN=https://dev.wikigdrive.com" \
-e "AUTH_INSTANCE=pr-${{ github.event.number }}" \
-e "DOMAIN=https://pr-${{ github.event.number }}.wikigdrive.com" \
--link=zipkin:zipkin \
"wikigdrive-feature:${{ github.sha }}" wikigdrive \
--service_account /service_account.json \
--share_email [email protected] \
--workdir /data \
server 3000
19 changes: 4 additions & 15 deletions src/containers/server/ServerContainer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import compress from 'compression';

import {Container, ContainerConfig, ContainerEngine} from '../../ContainerEngine.ts';
import {FileId} from '../../model/model.ts';
import {saveRunningInstance} from './loadRunningInstance.ts';
import {urlToFolderId} from '../../utils/idParsers.ts';
import {GoogleDriveService} from '../../google/GoogleDriveService.ts';
Expand Down Expand Up @@ -51,15 +50,6 @@
const HTML_DIR = __dirname + '/../../../apps/ui';
const MAIN_DIR = __dirname + '/../../..';

interface TreeItem {
id: FileId;
name: string;
mimeType: string;
children?: TreeItem[];
}

export const isHtml = req => req.headers.accept.indexOf('text/html') > -1;

function getDurationInMilliseconds(start) {
const NS_PER_SEC = 1e9;
const NS_TO_MS = 1e6;
Expand Down Expand Up @@ -98,7 +88,7 @@
limit: '50mb'
}));
app.use(express.text());
app.use(cookieParser());

Check failure

Code scanning / CodeQL

Missing CSRF middleware High

This cookie middleware is serving a
request handler
without CSRF protection.
This cookie middleware is serving a
request handler
without CSRF protection.
This cookie middleware is serving a
request handler
without CSRF protection.
This cookie middleware is serving a
request handler
without CSRF protection.
This cookie middleware is serving a
request handler
without CSRF protection.
This cookie middleware is serving a
request handler
without CSRF protection.
This cookie middleware is serving a
request handler
without CSRF protection.
This cookie middleware is serving a
request handler
without CSRF protection.

app.use((req, res, next) => {
res.header('GIT_SHA', process.env.GIT_SHA);
Expand Down Expand Up @@ -153,7 +143,7 @@

async initAuth(app) {
app.use('/auth/logout', authenticateOptionally(this.logger));
app.post('/auth/logout', async (req, res, next) => {
app.post('/auth/logout', async (req, res) => {
if (req.user?.google_access_token) {
const authClient = new UserAuthClient(process.env.GOOGLE_AUTH_CLIENT_ID, process.env.GOOGLE_AUTH_CLIENT_SECRET);
await authClient.revokeToken(req.user.google_access_token);
Expand All @@ -165,17 +155,16 @@

app.get('/auth/:driveId', async (req, res, next) => {
try {
const hostname = req.header('host');
const protocol = hostname.indexOf('localhost') > -1 ? 'http://' : 'https://';
const serverUrl = protocol + hostname;
const serverUrl = process.env.AUTH_DOMAIN || process.env.DOMAIN;
const driveId = urlToFolderId(req.params.driveId);
const redirectTo = req.query.redirectTo;
const popupWindow = req.query.popupWindow;

const state = new URLSearchParams(filterParams({
driveId: driveId !== 'none' ? (driveId || '') : '',
redirectTo,
popupWindow: popupWindow === 'true' ? 'true' : ''
popupWindow: popupWindow === 'true' ? 'true' : '',
instance: process.env.AUTH_INSTANCE
})).toString();

const authClient = new UserAuthClient(process.env.GOOGLE_AUTH_CLIENT_ID, process.env.GOOGLE_AUTH_CLIENT_SECRET);
Expand Down
54 changes: 32 additions & 22 deletions src/containers/server/auth.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import jsonwebtoken from 'jsonwebtoken';
import {decrypt, encrypt} from '../../google/GoogleAuthService';
import {GoogleDriveService} from '../../google/GoogleDriveService';
import {Logger} from 'winston';
import type {Request, Response} from 'express';
import {UserAuthClient} from '../../google/AuthClient';
import {FolderRegistryContainer} from '../folder_registry/FolderRegistryContainer';
import {urlToFolderId} from '../../utils/idParsers';
import {initJob, JobManagerContainer} from '../job/JobManagerContainer';
import {Logger} from 'winston';
import {decrypt, encrypt} from '../../google/GoogleAuthService.ts';
import {GoogleDriveService} from '../../google/GoogleDriveService.ts';
import {UserAuthClient} from '../../google/AuthClient.ts';
import {FolderRegistryContainer} from '../folder_registry/FolderRegistryContainer.ts';
import {urlToFolderId} from '../../utils/idParsers.ts';
import {initJob, JobManagerContainer} from '../job/JobManagerContainer.ts';

export class AuthError extends Error {
public status: number;
Expand All @@ -21,7 +21,7 @@

export function redirError(req: Request, msg: string) {
const err = new AuthError(msg + ' for: ' + req.originalUrl, 401);
const [empty, driveId] = req.path.split('/');
const [, driveId] = req.path.split('/');

const redirectTo: string = req.headers['redirect-to'] ? req.headers['redirect-to'].toString() : '';
if (redirectTo && redirectTo.startsWith('/') && redirectTo.indexOf('//') === -1) {
Expand Down Expand Up @@ -109,9 +109,7 @@

export async function handleDriveUiInstall(req: Request, res: Response, next) {
try {
const hostname = req.header('host');
const protocol = hostname.indexOf('localhost') > -1 ? 'http://' : 'https://';
const serverUrl = protocol + hostname;
const serverUrl = process.env.AUTH_DOMAIN || process.env.DOMAIN;

const state = new URLSearchParams(req.query.state.toString());
const driveui = urlToFolderId(state.get('driveui'));
Expand All @@ -129,9 +127,7 @@

export async function handleShare(req: Request, res: Response, next) {
try {
const hostname = req.header('host');
const protocol = hostname.indexOf('localhost') > -1 ? 'http://' : 'https://';
const serverUrl = protocol + hostname;
const serverUrl = process.env.AUTH_DOMAIN || process.env.DOMAIN;

const state = new URLSearchParams(req.query.state.toString());
const shareId = urlToFolderId(state.get('shareId'));
Expand All @@ -150,6 +146,14 @@
export async function handlePopupClose(req: Request, res: Response, next) {
try {
const state = new URLSearchParams(req.query.state.toString());
if (!process.env.AUTH_INSTANCE) { // main auth host
const instance = state.get('instance');
if (instance && instance.match(/^pr-\d+$/)) {
next();
return;
}
}

if (state.get('popupWindow') === 'true') {
openerRedirect(res, req.url.replace('popupWindow', ''));
return;
Expand Down Expand Up @@ -177,22 +181,28 @@
return `/drive/${folderId}`;
}

export async function getAuth(req, res: Response, next) {
export async function getAuth(req: Request, res: Response, next) {
try {
const hostname = req.header('host');
const protocol = hostname.indexOf('localhost') > -1 ? 'http://' : 'https://';
const serverUrl = protocol + hostname;
const serverUrl = process.env.AUTH_DOMAIN || process.env.DOMAIN;

const state = new URLSearchParams(req.query.state.toString());

if (!process.env.AUTH_INSTANCE) { // main auth host
const instance = state.get('instance');
if (instance && instance.match(/^pr-\d+$/)) {
res.redirect(`https://${instance}.wikigdrive.com${req.originalUrl}`);
return;
}
}

const driveId = urlToFolderId(state.get('driveId'));
const folderRegistryContainer = <FolderRegistryContainer>this.engine.getContainer('folder_registry');

const shareDrive = !!state.get('shareDrive');
if (driveId && shareDrive) {
const googleDriveService = new GoogleDriveService(this.logger, null);
const authClient = new UserAuthClient(process.env.GOOGLE_AUTH_CLIENT_ID, process.env.GOOGLE_AUTH_CLIENT_SECRET);
await authClient.authorizeResponseCode(req.query.code, `${serverUrl}/auth`);
await authClient.authorizeResponseCode(req.query.code.toString(), `${serverUrl}/auth`);

await googleDriveService.shareDrive(await authClient.getAccessToken(), driveId, this.params.share_email);

Expand All @@ -204,7 +214,7 @@
const uploadDrive = !!state.get('uploadDrive');
if (driveId && uploadDrive) {
const authClient = new UserAuthClient(process.env.GOOGLE_AUTH_CLIENT_ID, process.env.GOOGLE_AUTH_CLIENT_SECRET);
await authClient.authorizeResponseCode(req.query.code, `${serverUrl}/auth`);
await authClient.authorizeResponseCode(req.query.code.toString(), `${serverUrl}/auth`);

const jobManagerContainer = <JobManagerContainer>this.engine.getContainer('job_manager');
await jobManagerContainer.schedule(driveId, {
Expand All @@ -226,7 +236,7 @@
const redirectTo = sanitizeRedirect(state.get('redirectTo'));

const authClient = new UserAuthClient(process.env.GOOGLE_AUTH_CLIENT_ID, process.env.GOOGLE_AUTH_CLIENT_SECRET);
await authClient.authorizeResponseCode(req.query.code, `${serverUrl}/auth`);
await authClient.authorizeResponseCode(req.query.code.toString(), `${serverUrl}/auth`);
const googleDriveService = new GoogleDriveService(this.logger, null);
const googleUser: GoogleUser = await authClient.getUser(await authClient.getAccessToken());

Expand Down Expand Up @@ -257,9 +267,9 @@
} catch (err) {
if (err.message.indexOf('invalid_grant') > -1) {
if (req.query.state) {
const state = new URLSearchParams(req.query.state);
const state = new URLSearchParams(req.query.state.toString());
const redirectTo = state.get('redirectTo');
res.redirect(redirectTo || '/');

Check warning

Code scanning / CodeQL

Server-side URL redirect Medium

Untrusted URL redirection depends on a
user-provided value
.
} else {
res.redirect('/');
}
Expand Down
9 changes: 4 additions & 5 deletions src/containers/server/routes/Controller.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import type { Router } from 'express';
import type * as express from 'express';
import SwaggerDocService from './SwaggerDocService';
import winston from 'winston';
import {instrumentAndWrap} from '../../../telemetry';
import SwaggerDocService from './SwaggerDocService.ts';
import {instrumentAndWrap} from '../../../telemetry.ts';
// import SwaggerDocService from '../api-docs.api/SwaggerDocService';

export const HttpStatus = {
Expand Down Expand Up @@ -104,8 +103,8 @@ export class ErrorHandler implements ControllerCallContext {

export interface ControllerRoute {
errorHandlers: ErrorHandler[];
inputFilters: RouteFilter<any>[];
outputFilters: RouteFilter<any>[];
inputFilters: RouteFilter<unknown>[];
outputFilters: RouteFilter<unknown>[];
roles: string[];
method?: string;
routePath?: string;
Expand Down
Loading
Loading