BTHLABS-57: Pre-auth page in the extension
This commit is contained in:
parent
46254730bd
commit
495255206e
2
services/apple/.gitignore
vendored
2
services/apple/.gitignore
vendored
|
@ -90,3 +90,5 @@ Shared (Extension)/Resources/images/
|
||||||
Shared (Extension)/Resources/background-bundle.js
|
Shared (Extension)/Resources/background-bundle.js
|
||||||
Shared (Extension)/Resources/content-bundle.js
|
Shared (Extension)/Resources/content-bundle.js
|
||||||
Shared (Extension)/Resources/manifest.json
|
Shared (Extension)/Resources/manifest.json
|
||||||
|
Shared (Extension)/Resources/preauth.html
|
||||||
|
Shared (Extension)/Resources/preauth.js
|
||||||
|
|
|
@ -114,6 +114,8 @@
|
||||||
"Resources/content-bundle.js",
|
"Resources/content-bundle.js",
|
||||||
Resources/images,
|
Resources/images,
|
||||||
Resources/manifest.json,
|
Resources/manifest.json,
|
||||||
|
Resources/preauth.html,
|
||||||
|
Resources/preauth.js,
|
||||||
SafariWebExtensionHandler.m,
|
SafariWebExtensionHandler.m,
|
||||||
);
|
);
|
||||||
target = 4CABCAD42E56F0C900D8A354 /* HotPocket Extension (iOS) */;
|
target = 4CABCAD42E56F0C900D8A354 /* HotPocket Extension (iOS) */;
|
||||||
|
@ -126,6 +128,8 @@
|
||||||
"Resources/content-bundle.js",
|
"Resources/content-bundle.js",
|
||||||
Resources/images,
|
Resources/images,
|
||||||
Resources/manifest.json,
|
Resources/manifest.json,
|
||||||
|
Resources/preauth.html,
|
||||||
|
Resources/preauth.js,
|
||||||
SafariWebExtensionHandler.m,
|
SafariWebExtensionHandler.m,
|
||||||
);
|
);
|
||||||
target = 4CABCADE2E56F0C900D8A354 /* HotPocket Extension (macOS) */;
|
target = 4CABCADE2E56F0C900D8A354 /* HotPocket Extension (macOS) */;
|
||||||
|
|
|
@ -40,7 +40,9 @@ if (BASE_URL === null) {
|
||||||
|
|
||||||
const PLUGINS = [
|
const PLUGINS = [
|
||||||
string({
|
string({
|
||||||
include: /\.html$/,
|
include: [
|
||||||
|
'src/content/templates/*.html',
|
||||||
|
],
|
||||||
}),
|
}),
|
||||||
replace({
|
replace({
|
||||||
include: /\.js$/,
|
include: /\.js$/,
|
||||||
|
@ -115,6 +117,13 @@ export default [
|
||||||
src: 'assets/images',
|
src: 'assets/images',
|
||||||
dest: OUTPUT_PATH,
|
dest: OUTPUT_PATH,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
src: [
|
||||||
|
'src/content/preauth.html',
|
||||||
|
'src/content/preauth.js',
|
||||||
|
],
|
||||||
|
dest: OUTPUT_PATH,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,8 +1,23 @@
|
||||||
import HotPocketExtension from '../common';
|
import HotPocketExtension from '../common';
|
||||||
|
|
||||||
const AUTH_URL = (new URL('/integrations/extension/authenticate/', HotPocketExtension.base_url)).toString();
|
const POST_AUTH_PATH = '/integrations/extension/post-authenticate/';
|
||||||
const POST_AUTH_URL = (new URL('/integrations/extension/post-authenticate/', HotPocketExtension.base_url)).toString();
|
const RPC_PATH = '/rpc/';
|
||||||
const RPC_URL = (new URL('/rpc/', HotPocketExtension.base_url)).toString();
|
|
||||||
|
let authSessionToken = null;
|
||||||
|
let rpcURL = null;
|
||||||
|
|
||||||
|
const updateRpcURL = () => {
|
||||||
|
rpcURL = null;
|
||||||
|
if (HotPocketExtension.base_url !== null) {
|
||||||
|
rpcURL = (new URL(RPC_PATH, HotPocketExtension.base_url)).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
HotPocketExtension.LOGGER.debug(
|
||||||
|
'HotPocketExtension.background.updateRpcURL()',
|
||||||
|
HotPocketExtension.base_url,
|
||||||
|
rpcURL,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const makeJSONRPCCall = (method, params) => {
|
const makeJSONRPCCall = (method, params) => {
|
||||||
return {
|
return {
|
||||||
|
@ -43,7 +58,7 @@ const executeJSONRPCCall = async (url, call, {accessToken}) => {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
HotPocketExtension.LOGGER.debug(
|
HotPocketExtension.LOGGER.debug(
|
||||||
'HotPocketExtension.content.executeJSONRPCCall()', response,
|
'HotPocketExtension.background.executeJSONRPCCall()', response,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
|
@ -56,7 +71,7 @@ const executeJSONRPCCall = async (url, call, {accessToken}) => {
|
||||||
const callResult = await response.json();
|
const callResult = await response.json();
|
||||||
if (callResult.error) {
|
if (callResult.error) {
|
||||||
HotPocketExtension.LOGGER.error(
|
HotPocketExtension.LOGGER.error(
|
||||||
'HotPocketExtension.content.executeJSONRPCCall(): RPC error',
|
'HotPocketExtension.background.executeJSONRPCCall(): RPC error',
|
||||||
callResult.error.code,
|
callResult.error.code,
|
||||||
callResult.error.message,
|
callResult.error.message,
|
||||||
callResult.error.data,
|
callResult.error.data,
|
||||||
|
@ -69,7 +84,7 @@ const executeJSONRPCCall = async (url, call, {accessToken}) => {
|
||||||
}
|
}
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
HotPocketExtension.LOGGER.error(
|
HotPocketExtension.LOGGER.error(
|
||||||
'HotPocketExtension.content.executeJSONRPCCall(): Fetch error', exception,
|
'HotPocketExtension.background.executeJSONRPCCall(): Fetch error', exception,
|
||||||
);
|
);
|
||||||
error = {
|
error = {
|
||||||
code: -32000,
|
code: -32000,
|
||||||
|
@ -90,9 +105,9 @@ const getAccessTokenMeta = () => {
|
||||||
|
|
||||||
const doSave = async (accessToken, tab) => {
|
const doSave = async (accessToken, tab) => {
|
||||||
const call = makeJSONRPCCall('saves.create', [tab.url]);
|
const call = makeJSONRPCCall('saves.create', [tab.url]);
|
||||||
const [result, error] = await executeJSONRPCCall(RPC_URL, call, {accessToken});
|
const [result, error] = await executeJSONRPCCall(rpcURL, call, {accessToken});
|
||||||
HotPocketExtension.LOGGER.debug(
|
HotPocketExtension.LOGGER.debug(
|
||||||
'HotPocketExtension.content.doSave():', result, error,
|
'HotPocketExtension.background.doSave():', result, error,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (error !== null) {
|
if (error !== null) {
|
||||||
|
@ -112,7 +127,7 @@ const doCreateAndStoreAccessToken = async (authKey) => {
|
||||||
);
|
);
|
||||||
|
|
||||||
const [accessToken, error] = await executeJSONRPCCall(
|
const [accessToken, error] = await executeJSONRPCCall(
|
||||||
RPC_URL, accessTokenCall, {accessToken: null},
|
rpcURL, accessTokenCall, {accessToken: null},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (error === null) {
|
if (error === null) {
|
||||||
|
@ -125,15 +140,34 @@ const doCreateAndStoreAccessToken = async (authKey) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const doHandleAuthFlow = (authTab) => {
|
const doHandleAuthFlow = (authTab) => {
|
||||||
|
HotPocketExtension.LOGGER.debug(
|
||||||
|
'HotPocketExtension.background.doHandleAuthFlow()', authTab,
|
||||||
|
);
|
||||||
|
let currentAuthTabId = authTab.id;
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const onTabsUpdated = (tabId, changeInfo, updatedTab) => {
|
const onTabsUpdated = (tabId, changeInfo, updatedTab) => {
|
||||||
if (tabId === authTab.id) {
|
const changedURL = changeInfo.url;
|
||||||
HotPocketExtension.LOGGER.debug(
|
|
||||||
'HotPocketExtension.content.doHandleAuthFlow.onTabsUpdated()', updatedTab, changeInfo,
|
|
||||||
);
|
|
||||||
|
|
||||||
const changedURL = changeInfo.url;
|
HotPocketExtension.LOGGER.debug(
|
||||||
if (changedURL && changedURL.startsWith(POST_AUTH_URL)) {
|
'HotPocketExtension.background.doHandleAuthFlow.onTabsUpdated()',
|
||||||
|
updatedTab,
|
||||||
|
changedURL,
|
||||||
|
(changedURL && changedURL.includes(POST_AUTH_PATH)),
|
||||||
|
changeInfo,
|
||||||
|
);
|
||||||
|
|
||||||
|
const expectedSessionTabQuery = `?authSessionToken=${authSessionToken}`;
|
||||||
|
if (tabId !== currentAuthTabId && changedURL.includes(expectedSessionTabQuery)) {
|
||||||
|
// When redirecting from the preauth page to the HotPocket instance,
|
||||||
|
// Safari "replaces" the auth tab with a new one. This nasty hack will
|
||||||
|
// allow the extension to keep track of it.
|
||||||
|
// I hate computers.
|
||||||
|
currentAuthTabId = tabId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tabId === currentAuthTabId) {
|
||||||
|
if (changedURL && changedURL.includes(POST_AUTH_PATH)) {
|
||||||
const parsedChangedURL = new URL(changedURL);
|
const parsedChangedURL = new URL(changedURL);
|
||||||
const authKey = parsedChangedURL.searchParams.get('auth_key');
|
const authKey = parsedChangedURL.searchParams.get('auth_key');
|
||||||
|
|
||||||
|
@ -154,8 +188,9 @@ const doHandleAuthFlow = (authTab) => {
|
||||||
reject(error);
|
reject(error);
|
||||||
}).
|
}).
|
||||||
finally(() => {
|
finally(() => {
|
||||||
|
authSessionToken = null;
|
||||||
HotPocketExtension.api.tabs.onUpdated.removeListener(onTabsUpdated);
|
HotPocketExtension.api.tabs.onUpdated.removeListener(onTabsUpdated);
|
||||||
HotPocketExtension.api.tabs.remove(authTab.id);
|
HotPocketExtension.api.tabs.remove(currentAuthTabId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -175,7 +210,7 @@ const doCheckAuth = async (accessToken) => {
|
||||||
[accessToken, getAccessTokenMeta()],
|
[accessToken, getAccessTokenMeta()],
|
||||||
);
|
);
|
||||||
|
|
||||||
const [result, error] = await executeJSONRPCCall(RPC_URL, call, {
|
const [result, error] = await executeJSONRPCCall(rpcURL, call, {
|
||||||
accessToken,
|
accessToken,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -196,15 +231,28 @@ const doCheckAuth = async (accessToken) => {
|
||||||
return accessToken;
|
return accessToken;
|
||||||
};
|
};
|
||||||
|
|
||||||
const doGetAccessToken = async () => {
|
const doSetupRPC = async () => {
|
||||||
let storageResult = await HotPocketExtension.api.storage.local.get('accessToken');
|
let storageResult = await HotPocketExtension.api.storage.local.get(
|
||||||
let accessToken = await doCheckAuth(
|
['accessToken', 'baseURL'],
|
||||||
storageResult.accessToken || null,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let accessToken = null;
|
||||||
|
if (storageResult.baseURL) {
|
||||||
|
HotPocketExtension.base_url = storageResult.baseURL;
|
||||||
|
updateRpcURL();
|
||||||
|
|
||||||
|
accessToken = await doCheckAuth(
|
||||||
|
storageResult.accessToken || null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (accessToken === null) {
|
if (accessToken === null) {
|
||||||
|
authSessionToken = crypto.randomUUID();
|
||||||
|
|
||||||
const authTab = await HotPocketExtension.api.tabs.create({
|
const authTab = await HotPocketExtension.api.tabs.create({
|
||||||
url: AUTH_URL,
|
url: HotPocketExtension.api.runtime.getURL(
|
||||||
|
`preauth.html?authSessionToken=${authSessionToken}`,
|
||||||
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
accessToken = await doHandleAuthFlow(authTab);
|
accessToken = await doHandleAuthFlow(authTab);
|
||||||
|
@ -217,24 +265,53 @@ const doSendTabMessage = (tab, message) => {
|
||||||
HotPocketExtension.api.tabs.sendMessage(tab.id, message).
|
HotPocketExtension.api.tabs.sendMessage(tab.id, message).
|
||||||
then((result) => {
|
then((result) => {
|
||||||
HotPocketExtension.LOGGER.debug(
|
HotPocketExtension.LOGGER.debug(
|
||||||
'HotPocketExtension.content.doSendTabMessage(): message sent', message, result,
|
'HotPocketExtension.background.doSendTabMessage(): message sent',
|
||||||
|
message,
|
||||||
|
result,
|
||||||
);
|
);
|
||||||
}).
|
}).
|
||||||
catch((error) => {
|
catch((error) => {
|
||||||
HotPocketExtension.LOGGER.error(
|
HotPocketExtension.LOGGER.error(
|
||||||
'HotPocketExtension.content.doSendTabMessage(): could not send message', error,
|
'HotPocketExtension.background.doSendTabMessage(): could not send message',
|
||||||
|
error,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const doUpdateBaseURL = (nextBaseURL) => {
|
||||||
|
HotPocketExtension.base_url = nextBaseURL;
|
||||||
|
updateRpcURL();
|
||||||
|
|
||||||
|
HotPocketExtension.api.storage.local.
|
||||||
|
set({
|
||||||
|
baseURL: nextBaseURL,
|
||||||
|
}).
|
||||||
|
then(() => {
|
||||||
|
HotPocketExtension.LOGGER.debug(
|
||||||
|
'HotPocketExtension.background.doUpdateBaseURL()', 'Base URL saved',
|
||||||
|
);
|
||||||
|
}).
|
||||||
|
catch((error) => {
|
||||||
|
HotPocketExtension.LOGGER.error(
|
||||||
|
'HotPocketExtension.background.doUpdateBaseURL()', error,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const onTabCreated = (tab) => {
|
const onTabCreated = (tab) => {
|
||||||
HotPocketExtension.LOGGER.debug('HotPocketExtension.onTabCreated()', tab);
|
HotPocketExtension.LOGGER.debug(
|
||||||
HotPocketExtension.api.action.enable(tab.id);
|
'HotPocketExtension.background.onTabCreated()', tab,
|
||||||
|
);
|
||||||
|
HotPocketExtension.api.action.enable(tab.id).catch((error) => {
|
||||||
|
HotPocketExtension.LOGGER.error(
|
||||||
|
'HotPocketExtension.background.onTabCreated()', tab.id, error,
|
||||||
|
);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const onBrowserActionClicked = async (tab) => {
|
const onBrowserActionClicked = async (tab) => {
|
||||||
HotPocketExtension.LOGGER.debug(
|
HotPocketExtension.LOGGER.debug(
|
||||||
'HotPocketExtension.onBrowserActionClicked()', tab.url,
|
'HotPocketExtension.background.onBrowserActionClicked()', tab.url,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!tab.url) {
|
if (!tab.url) {
|
||||||
|
@ -245,11 +322,11 @@ const onBrowserActionClicked = async (tab) => {
|
||||||
let error = null;
|
let error = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let accessToken = await doGetAccessToken();
|
let accessToken = await doSetupRPC();
|
||||||
|
|
||||||
result = await doSave(accessToken, tab);
|
result = await doSave(accessToken, tab);
|
||||||
HotPocketExtension.LOGGER.debug(
|
HotPocketExtension.LOGGER.debug(
|
||||||
'HotPocketExtension.onBrowserActionClicked()', result,
|
'HotPocketExtension.background.onBrowserActionClicked()', result,
|
||||||
);
|
);
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
HotPocketExtension.LOGGER.error(
|
HotPocketExtension.LOGGER.error(
|
||||||
|
@ -276,10 +353,11 @@ const onMessage = (message, sender, sendResponse) => {
|
||||||
let response = {ok: true};
|
let response = {ok: true};
|
||||||
try {
|
try {
|
||||||
if (message.type === 'HotPocket:Extension:ping') {
|
if (message.type === 'HotPocket:Extension:ping') {
|
||||||
HotPocketExtension.LOGGER.debug(sender.tab.id);
|
|
||||||
doSendTabMessage(sender.tab, {
|
doSendTabMessage(sender.tab, {
|
||||||
type: 'HotPocket:Extension:pong',
|
type: 'HotPocket:Extension:pong',
|
||||||
});
|
});
|
||||||
|
} else if (message.type === 'HotPocket:Extension:setBaseURL') {
|
||||||
|
doUpdateBaseURL(message.result);
|
||||||
}
|
}
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
HotPocketExtension.LOGGER.error(
|
HotPocketExtension.LOGGER.error(
|
||||||
|
@ -300,6 +378,8 @@ export default ({...configuration}) => {
|
||||||
background: true,
|
background: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
updateRpcURL();
|
||||||
|
|
||||||
HotPocketExtension.api.tabs.onCreated.addListener(onTabCreated);
|
HotPocketExtension.api.tabs.onCreated.addListener(onTabCreated);
|
||||||
|
|
||||||
HotPocketExtension.api.action.onClicked.addListener(onBrowserActionClicked);
|
HotPocketExtension.api.action.onClicked.addListener(onBrowserActionClicked);
|
||||||
|
|
|
@ -8,7 +8,7 @@ const HotPocketExtension = {
|
||||||
version: __HOTPOCKET_EXTENSION_VERSION__,
|
version: __HOTPOCKET_EXTENSION_VERSION__,
|
||||||
debug: DEBUG,
|
debug: DEBUG,
|
||||||
api: null,
|
api: null,
|
||||||
base_url: __HOTPOCKET_EXTENSION_BASE_URL__,
|
base_url: null,
|
||||||
LOGGER: {
|
LOGGER: {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
debug: (DEBUG === true) ? console.log : noop,
|
debug: (DEBUG === true) ? console.log : noop,
|
||||||
|
|
91
services/extension/src/content/preauth.html
Normal file
91
services/extension/src/content/preauth.html
Normal file
File diff suppressed because one or more lines are too long
45
services/extension/src/content/preauth.js
Normal file
45
services/extension/src/content/preauth.js
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
/*!
|
||||||
|
* HotPocket by BTHLabs (https://hotpocket.app/)
|
||||||
|
* Copyright 2025-present BTHLabs <contact@bthlabs.pl> (https://bthlabs.pl/)
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
(() => {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', (event) => {
|
||||||
|
const form = document.getElementById('PreauthForm');
|
||||||
|
form.addEventListener('submit', (event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const inputBaseURL = document.getElementById('id_base_url');
|
||||||
|
const baseURL = inputBaseURL.value;
|
||||||
|
|
||||||
|
let api = window.browser || null;
|
||||||
|
if (api === null && window.chrome) {
|
||||||
|
api = window.chrome;
|
||||||
|
}
|
||||||
|
|
||||||
|
api.runtime.sendMessage({
|
||||||
|
type: 'HotPocket:Extension:setBaseURL',
|
||||||
|
result: baseURL,
|
||||||
|
});
|
||||||
|
|
||||||
|
const loginURL = new URL('/integrations/extension/authenticate/', inputBaseURL.value);
|
||||||
|
window.location.replace(loginURL.toString());
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})();
|
|
@ -19,7 +19,7 @@
|
||||||
"data_collection_permissions": {
|
"data_collection_permissions": {
|
||||||
"required": [
|
"required": [
|
||||||
"websiteActivity",
|
"websiteActivity",
|
||||||
"technicalAndInteraction"
|
"browsingActivity"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
48
tasks.py
48
tasks.py
|
@ -1,9 +1,10 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import base64
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from invoke import task
|
from invoke import Context, task
|
||||||
|
|
||||||
from hotpocket_workspace_tools import get_workspace_mode, WorkspaceMode
|
from hotpocket_workspace_tools import get_workspace_mode, WorkspaceMode
|
||||||
|
|
||||||
|
@ -63,7 +64,7 @@ WORKSPACE_MODE = get_workspace_mode()
|
||||||
ALL_SERVICES = ['backend', 'packages', 'extension']
|
ALL_SERVICES = ['backend', 'packages', 'extension']
|
||||||
|
|
||||||
|
|
||||||
def _run_in_service(ctx, service, command, **kwargs):
|
def _run_in_service(ctx: Context, service, command, **kwargs):
|
||||||
match WORKSPACE_MODE:
|
match WORKSPACE_MODE:
|
||||||
case WorkspaceMode.DOCKER:
|
case WorkspaceMode.DOCKER:
|
||||||
return ctx.run(
|
return ctx.run(
|
||||||
|
@ -75,7 +76,7 @@ def _run_in_service(ctx, service, command, **kwargs):
|
||||||
ctx.run(f'direnv exec . {command}')
|
ctx.run(f'direnv exec . {command}')
|
||||||
|
|
||||||
|
|
||||||
def _get_version(ctx, service):
|
def _get_version(ctx: Context, service):
|
||||||
with ctx.cd(f'services/{service}'):
|
with ctx.cd(f'services/{service}'):
|
||||||
run_result = ctx.run('poetry version -s', hide='out')
|
run_result = ctx.run('poetry version -s', hide='out')
|
||||||
|
|
||||||
|
@ -88,27 +89,27 @@ def _get_head_sha(ctx):
|
||||||
|
|
||||||
|
|
||||||
@task
|
@task
|
||||||
def clean(ctx, service):
|
def clean(ctx: Context, service):
|
||||||
_run_in_service(ctx, service, 'inv clean')
|
_run_in_service(ctx, service, 'inv clean')
|
||||||
|
|
||||||
|
|
||||||
@task
|
@task
|
||||||
def test(ctx, service):
|
def test(ctx: Context, service):
|
||||||
_run_in_service(ctx, service, 'inv test')
|
_run_in_service(ctx, service, 'inv test')
|
||||||
|
|
||||||
|
|
||||||
@task
|
@task
|
||||||
def lint(ctx, service):
|
def lint(ctx: Context, service):
|
||||||
_run_in_service(ctx, service, 'inv lint')
|
_run_in_service(ctx, service, 'inv lint')
|
||||||
|
|
||||||
|
|
||||||
@task
|
@task
|
||||||
def typecheck(ctx, service):
|
def typecheck(ctx: Context, service):
|
||||||
_run_in_service(ctx, service, 'inv typecheck')
|
_run_in_service(ctx, service, 'inv typecheck')
|
||||||
|
|
||||||
|
|
||||||
@task
|
@task
|
||||||
def shell(ctx, service):
|
def shell(ctx: Context, service):
|
||||||
assert WORKSPACE_MODE == WorkspaceMode.DOCKER, (
|
assert WORKSPACE_MODE == WorkspaceMode.DOCKER, (
|
||||||
'Just `cd services/{service}` ;)'
|
'Just `cd services/{service}` ;)'
|
||||||
)
|
)
|
||||||
|
@ -116,12 +117,21 @@ def shell(ctx, service):
|
||||||
|
|
||||||
|
|
||||||
@task
|
@task
|
||||||
def django_shell(ctx, service):
|
def django_shell(ctx: Context, service):
|
||||||
_run_in_service(ctx, service, 'inv django-shell')
|
_run_in_service(ctx, service, 'inv django-shell')
|
||||||
|
|
||||||
|
|
||||||
@task
|
@task
|
||||||
def build(ctx,
|
def png_to_data_url(ctx: Context, png_path):
|
||||||
|
with open(png_path, 'rb') as png_f:
|
||||||
|
data = png_f.read()
|
||||||
|
|
||||||
|
encoded_data = base64.b64encode(data)
|
||||||
|
print(f'data:image/png;base64,{encoded_data.decode("utf-8")}')
|
||||||
|
|
||||||
|
|
||||||
|
@task
|
||||||
|
def build(ctx: Context,
|
||||||
service,
|
service,
|
||||||
context=None,
|
context=None,
|
||||||
builder=None,
|
builder=None,
|
||||||
|
@ -175,7 +185,7 @@ def build(ctx,
|
||||||
|
|
||||||
|
|
||||||
@task
|
@task
|
||||||
def publish(ctx,
|
def publish(ctx: Context,
|
||||||
service,
|
service,
|
||||||
context=None,
|
context=None,
|
||||||
target='deployment',
|
target='deployment',
|
||||||
|
@ -199,12 +209,12 @@ def publish(ctx,
|
||||||
|
|
||||||
|
|
||||||
@task
|
@task
|
||||||
def ci(ctx, service):
|
def ci(ctx: Context, service):
|
||||||
_run_in_service(ctx, service, 'inv ci')
|
_run_in_service(ctx, service, 'inv ci')
|
||||||
|
|
||||||
|
|
||||||
@task
|
@task
|
||||||
def setup(ctx, service=None):
|
def setup(ctx: Context, service=None):
|
||||||
services_to_setup = []
|
services_to_setup = []
|
||||||
|
|
||||||
services_to_setup = [*ALL_SERVICES]
|
services_to_setup = [*ALL_SERVICES]
|
||||||
|
@ -216,7 +226,7 @@ def setup(ctx, service=None):
|
||||||
|
|
||||||
|
|
||||||
@task
|
@task
|
||||||
def install(ctx, service=None):
|
def install(ctx: Context, service=None):
|
||||||
services_to_setup = []
|
services_to_setup = []
|
||||||
|
|
||||||
services_to_setup = [*ALL_SERVICES]
|
services_to_setup = [*ALL_SERVICES]
|
||||||
|
@ -228,7 +238,7 @@ def install(ctx, service=None):
|
||||||
|
|
||||||
|
|
||||||
@task
|
@task
|
||||||
def lock(ctx, service):
|
def lock(ctx: Context, service):
|
||||||
_run_in_service(ctx, service, 'poetry lock --no-update')
|
_run_in_service(ctx, service, 'poetry lock --no-update')
|
||||||
|
|
||||||
|
|
||||||
|
@ -238,20 +248,20 @@ def start_cloud(ctx):
|
||||||
|
|
||||||
|
|
||||||
@task
|
@task
|
||||||
def start_web(ctx, service):
|
def start_web(ctx: Context, service):
|
||||||
_run_in_service(ctx, service, 'inv start-web')
|
_run_in_service(ctx, service, 'inv start-web')
|
||||||
|
|
||||||
|
|
||||||
@task
|
@task
|
||||||
def start_celery_worker(ctx, service):
|
def start_celery_worker(ctx: Context, service):
|
||||||
_run_in_service(ctx, service, 'inv start-worker')
|
_run_in_service(ctx, service, 'inv start-worker')
|
||||||
|
|
||||||
|
|
||||||
@task
|
@task
|
||||||
def start_celery_beat(ctx, service):
|
def start_celery_beat(ctx: Context, service):
|
||||||
_run_in_service(ctx, service, 'inv start-beat')
|
_run_in_service(ctx, service, 'inv start-beat')
|
||||||
|
|
||||||
|
|
||||||
@task
|
@task
|
||||||
def start_app(ctx, service, app):
|
def start_app(ctx: Context, service, app):
|
||||||
_run_in_service(ctx, service, f'inv start-{app}')
|
_run_in_service(ctx, service, f'inv start-{app}')
|
||||||
|
|
Loading…
Reference in New Issue
Block a user