fixes and secure storage

This commit is contained in:
Dmitry 2025-11-23 03:00:32 +03:00
parent c287d77cf8
commit 1bfdd73389
21 changed files with 75079 additions and 68225 deletions

View file

@ -202,6 +202,17 @@ jobs:
systemctl status mnemo_cards_server --no-pager
ENDSSH
- name: Generate and Deploy Nginx Configs
run: |
ssh -o ServerAliveInterval=60 -o ServerAliveCountMax=3 -o ConnectTimeout=30 -o StrictHostKeyChecking=no ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} << 'ENDSSH'
set -e
echo "🔧 Generating and deploying nginx configurations..."
cd ~/tools/deploy
./generate-nginx-configs.sh
./deploy-nginx-configs.sh
./check-nginx.sh
ENDSSH
- name: Ensure Nginx Running
run: |
ssh -o ServerAliveInterval=60 -o ServerAliveCountMax=3 -o ConnectTimeout=30 -o StrictHostKeyChecking=no ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} << 'ENDSSH'

View file

@ -1 +1 @@
1c4dc119e18bcf1ad36e5635fba58ce0
cbc289e61044aa6e6f63e7abe894d47e

View file

@ -1 +1 @@
1../mnemo_cards/fonts/Nunito-VariableFont_wght.ttf  asset1../mnemo_cards/fonts/Nunito-VariableFont_wght.ttf2packages/cupertino_icons/assets/CupertinoIcons.ttf  asset2packages/cupertino_icons/assets/CupertinoIcons.ttf
1../mnemo_cards/fonts/Nunito-VariableFont_wght.ttf  asset1../mnemo_cards/fonts/Nunito-VariableFont_wght.ttf icons/ad.webp  asset icons/ad.webpicons/back.webp  asseticons/back.webpicons/cards.webp  asseticons/cards.webpicons/chat.webp  asseticons/chat.webpicons/clock.webp  asseticons/clock.webpicons/crown.webp  asseticons/crown.webpicons/down.webp  asseticons/down.webpicons/download.webp  asseticons/download.webpicons/exit.webp  asseticons/exit.webpicons/exit2.webp  asseticons/exit2.webpicons/exit3.webp  asseticons/exit3.webpicons/gift.webp  asseticons/gift.webpicons/google.webp  asseticons/google.webpicons/heart.webp  asseticons/heart.webpicons/heart_fill.webp  asset1../mnemo_cards/fonts/Nunito-VariableFont_wght.ttf1../mnemo_cards/fonts/Nunito-VariableFont_wght.ttf  asset1../mnemo_cards/fonts/Nunito-VariableFont_wght.ttfasset1../mnemo_cards/fonts/Nunito-VariableFont_wght.ttfasseticons/mic_off.webpicons/mic_on.webp  asseticons/mic_on.webpicons/next.webp  asseticons/next.webpicons/profile.webp  asseticons/profile.webpicons/settings.webp  asseticons/settings.webpicons/shuffle.webp  asseticons/shuffle.webpicons/skull.webp  asseticons/skull.webpicons/small_circle.webp  asseticons/small_circle.webpicons/sound_off.webp  asseticons/sound_off.webpicons/sound_on.webp  asseticons/sound_on.webpicons/telegram.webp  asseticons/telegram.webp icons/tg.webp  asset icons/tg.webpicons/view.webp  asseticons/view.webp2packages/cupertino_icons/assets/CupertinoIcons.ttf  asset2packages/cupertino_icons/assets/CupertinoIcons.ttf

View file

@ -1 +1 @@
"DQIHMS4uL21uZW1vX2NhcmRzL2ZvbnRzL051bml0by1WYXJpYWJsZUZvbnRfd2dodC50dGYMAQ0BBwVhc3NldAcxLi4vbW5lbW9fY2FyZHMvZm9udHMvTnVuaXRvLVZhcmlhYmxlRm9udF93Z2h0LnR0ZgcycGFja2FnZXMvY3VwZXJ0aW5vX2ljb25zL2Fzc2V0cy9DdXBlcnRpbm9JY29ucy50dGYMAQ0BBwVhc3NldAcycGFja2FnZXMvY3VwZXJ0aW5vX2ljb25zL2Fzc2V0cy9DdXBlcnRpbm9JY29ucy50dGY="
"DR8HMS4uL21uZW1vX2NhcmRzL2ZvbnRzL051bml0by1WYXJpYWJsZUZvbnRfd2dodC50dGYMAQ0BBwVhc3NldAcxLi4vbW5lbW9fY2FyZHMvZm9udHMvTnVuaXRvLVZhcmlhYmxlRm9udF93Z2h0LnR0ZgcNaWNvbnMvYWQud2VicAwBDQEHBWFzc2V0Bw1pY29ucy9hZC53ZWJwBw9pY29ucy9iYWNrLndlYnAMAQ0BBwVhc3NldAcPaWNvbnMvYmFjay53ZWJwBxBpY29ucy9jYXJkcy53ZWJwDAENAQcFYXNzZXQHEGljb25zL2NhcmRzLndlYnAHD2ljb25zL2NoYXQud2VicAwBDQEHBWFzc2V0Bw9pY29ucy9jaGF0LndlYnAHEGljb25zL2Nsb2NrLndlYnAMAQ0BBwVhc3NldAcQaWNvbnMvY2xvY2sud2VicAcQaWNvbnMvY3Jvd24ud2VicAwBDQEHBWFzc2V0BxBpY29ucy9jcm93bi53ZWJwBw9pY29ucy9kb3duLndlYnAMAQ0BBwVhc3NldAcPaWNvbnMvZG93bi53ZWJwBxNpY29ucy9kb3dubG9hZC53ZWJwDAENAQcFYXNzZXQHE2ljb25zL2Rvd25sb2FkLndlYnAHD2ljb25zL2V4aXQud2VicAwBDQEHBWFzc2V0Bw9pY29ucy9leGl0LndlYnAHEGljb25zL2V4aXQyLndlYnAMAQ0BBwVhc3NldAcQaWNvbnMvZXhpdDIud2VicAcQaWNvbnMvZXhpdDMud2VicAwBDQEHBWFzc2V0BxBpY29ucy9leGl0My53ZWJwBw9pY29ucy9naWZ0LndlYnAMAQ0BBwVhc3NldAcPaWNvbnMvZ2lmdC53ZWJwBxFpY29ucy9nb29nbGUud2VicAwBDQEHBWFzc2V0BxFpY29ucy9nb29nbGUud2VicAcQaWNvbnMvaGVhcnQud2VicAwBDQEHBWFzc2V0BxBpY29ucy9oZWFydC53ZWJwBxVpY29ucy9oZWFydF9maWxsLndlYnAMAQ0BBwVhc3NldAcVaWNvbnMvaGVhcnRfZmlsbC53ZWJwBw9pY29ucy9sb2NrLndlYnAMAQ0BBwVhc3NldAcPaWNvbnMvbG9jay53ZWJwBxJpY29ucy9taWNfb2ZmLndlYnAMAQ0BBwVhc3NldAcSaWNvbnMvbWljX29mZi53ZWJwBxFpY29ucy9taWNfb24ud2VicAwBDQEHBWFzc2V0BxFpY29ucy9taWNfb24ud2VicAcPaWNvbnMvbmV4dC53ZWJwDAENAQcFYXNzZXQHD2ljb25zL25leHQud2VicAcSaWNvbnMvcHJvZmlsZS53ZWJwDAENAQcFYXNzZXQHEmljb25zL3Byb2ZpbGUud2VicAcTaWNvbnMvc2V0dGluZ3Mud2VicAwBDQEHBWFzc2V0BxNpY29ucy9zZXR0aW5ncy53ZWJwBxJpY29ucy9zaHVmZmxlLndlYnAMAQ0BBwVhc3NldAcSaWNvbnMvc2h1ZmZsZS53ZWJwBxBpY29ucy9za3VsbC53ZWJwDAENAQcFYXNzZXQHEGljb25zL3NrdWxsLndlYnAHF2ljb25zL3NtYWxsX2NpcmNsZS53ZWJwDAENAQcFYXNzZXQHF2ljb25zL3NtYWxsX2NpcmNsZS53ZWJwBxRpY29ucy9zb3VuZF9vZmYud2VicAwBDQEHBWFzc2V0BxRpY29ucy9zb3VuZF9vZmYud2VicAcTaWNvbnMvc291bmRfb24ud2VicAwBDQEHBWFzc2V0BxNpY29ucy9zb3VuZF9vbi53ZWJwBxNpY29ucy90ZWxlZ3JhbS53ZWJwDAENAQcFYXNzZXQHE2ljb25zL3RlbGVncmFtLndlYnAHDWljb25zL3RnLndlYnAMAQ0BBwVhc3NldAcNaWNvbnMvdGcud2VicAcPaWNvbnMvdmlldy53ZWJwDAENAQcFYXNzZXQHD2ljb25zL3ZpZXcud2VicAcycGFja2FnZXMvY3VwZXJ0aW5vX2ljb25zL2Fzc2V0cy9DdXBlcnRpbm9JY29ucy50dGYMAQ0BBwVhc3NldAcycGFja2FnZXMvY3VwZXJ0aW5vX2ljb25zL2Fzc2V0cy9DdXBlcnRpbm9JY29ucy50dGY="

View file

@ -1 +1 @@
{"../mnemo_cards/fonts/Nunito-VariableFont_wght.ttf":["../mnemo_cards/fonts/Nunito-VariableFont_wght.ttf"],"packages/cupertino_icons/assets/CupertinoIcons.ttf":["packages/cupertino_icons/assets/CupertinoIcons.ttf"]}
{"../mnemo_cards/fonts/Nunito-VariableFont_wght.ttf":["../mnemo_cards/fonts/Nunito-VariableFont_wght.ttf"],"icons/ad.webp":["icons/ad.webp"],"icons/back.webp":["icons/back.webp"],"icons/cards.webp":["icons/cards.webp"],"icons/chat.webp":["icons/chat.webp"],"icons/clock.webp":["icons/clock.webp"],"icons/crown.webp":["icons/crown.webp"],"icons/down.webp":["icons/down.webp"],"icons/download.webp":["icons/download.webp"],"icons/exit.webp":["icons/exit.webp"],"icons/exit2.webp":["icons/exit2.webp"],"icons/exit3.webp":["icons/exit3.webp"],"icons/gift.webp":["icons/gift.webp"],"icons/google.webp":["icons/google.webp"],"icons/heart.webp":["icons/heart.webp"],"icons/heart_fill.webp":["icons/heart_fill.webp"],"icons/lock.webp":["icons/lock.webp"],"icons/mic_off.webp":["icons/mic_off.webp"],"icons/mic_on.webp":["icons/mic_on.webp"],"icons/next.webp":["icons/next.webp"],"icons/profile.webp":["icons/profile.webp"],"icons/settings.webp":["icons/settings.webp"],"icons/shuffle.webp":["icons/shuffle.webp"],"icons/skull.webp":["icons/skull.webp"],"icons/small_circle.webp":["icons/small_circle.webp"],"icons/sound_off.webp":["icons/sound_off.webp"],"icons/sound_on.webp":["icons/sound_on.webp"],"icons/telegram.webp":["icons/telegram.webp"],"icons/tg.webp":["icons/tg.webp"],"icons/view.webp":["icons/view.webp"],"packages/cupertino_icons/assets/CupertinoIcons.ttf":["packages/cupertino_icons/assets/CupertinoIcons.ttf"]}

View file

@ -38,6 +38,6 @@ _flutter.buildConfig = {"engineRevision":"d3d45dcf251823c1769909cd43698d126db38d
_flutter.loader.load({
serviceWorkerSettings: {
serviceWorkerVersion: "2237867940"
serviceWorkerVersion: "1664190830"
}
});

View file

@ -3,11 +3,11 @@ const MANIFEST = 'flutter-app-manifest';
const TEMP = 'flutter-temp-cache';
const CACHE_NAME = 'flutter-app-cache';
const RESOURCES = {"flutter_bootstrap.js": "7833490e08899f1a73e0d1a28dce5806",
const RESOURCES = {"flutter_bootstrap.js": "1016c5edf68382e5cd6bc30dc8abc968",
"version.json": "28542ad255b8c0abed0b83ec843a2417",
"index.html": "2414db4ab7b157a70906335536c9b12f",
"/": "2414db4ab7b157a70906335536c9b12f",
"main.dart.js": "3fb19ec457a2d8c0efd8fa87eaed0804",
"index.html": "bea65743d155de5b43dd38c2a02fa14d",
"/": "bea65743d155de5b43dd38c2a02fa14d",
"main.dart.js": "efd61556cfb88ace9d0e42213140da3c",
"flutter.js": "888483df48293866f9f41d3d9274a779",
"mnemo_cards/fonts/Nunito-VariableFont_wght.ttf": "ea0ad4c72a135f9a43ec7bb83f2469aa",
"favicon.png": "5dcef449791fa27946b3d35ad8803796",
@ -16,15 +16,44 @@ const RESOURCES = {"flutter_bootstrap.js": "7833490e08899f1a73e0d1a28dce5806",
"icons/Icon-maskable-512.png": "301a7604d45b3e739efc881eb04896ea",
"icons/Icon-512.png": "96e752610906ba2a93c65f8abe1645f1",
"manifest.json": "52ae309b087ade76dc53b15e820872a5",
"foos.js": "da94bf5c2c30afde57065bf69154070a",
"assets/AssetManifest.json": "d858d53ad6fe5329b2552f4f1c8ef480",
"foos.js": "15c33cb4bb47bf3dbeae9bbe5c330796",
"assets/AssetManifest.json": "4446605ce656b304ed309216b1cb0766",
"assets/NOTICES": "9a4cb06d9c96803dbf84a97820925f74",
"assets/FontManifest.json": "39e62425377b8c56565d232f537294b4",
"assets/AssetManifest.bin.json": "7493ed26bdf665567c349d7c0c2862b1",
"assets/AssetManifest.bin.json": "75e03a20d827286422d0de35a65b7ff1",
"assets/icons/google.webp": "9481bad94ab5aec99bdecac8faa569bd",
"assets/icons/sound_on.webp": "ab2e4d5e335c7ab92eb6513f1196df7e",
"assets/icons/profile.webp": "9a9e7ad8b4f2303e9d5f92c0129724af",
"assets/icons/shuffle.webp": "cde1214b225c40111f69392d15817c31",
"assets/icons/heart_fill.webp": "b6e8d7faa8975d9eb682db95eeaf9eb0",
"assets/icons/mic_off.webp": "d5dc7d727187c8a8a23040687398366e",
"assets/icons/heart.webp": "f6c6182054002a5e38e9f4dc001240ba",
"assets/icons/telegram.webp": "709dc006e25be6b47163d64ae0b9f237",
"assets/icons/skull.webp": "b17661c79a540f5f86594c619b2d3b7a",
"assets/icons/back.webp": "8f20891568ae5a93b84e9c3d6beccbe7",
"assets/icons/cards.webp": "4d8b07569e721a06174d28dabf480514",
"assets/icons/small_circle.webp": "77c43c50bc56e2476ec6beec1776ce3b",
"assets/icons/crown.webp": "935ac28b3a9ea73c60048c515c8794b7",
"assets/icons/download.webp": "7848632925770aa35bbd74839f5fc14a",
"assets/icons/mic_on.webp": "ac59064c2351867e8cba3e56ecaa08f1",
"assets/icons/view.webp": "004ff539554b79f3be0d828b8d6eb518",
"assets/icons/next.webp": "7b954a801178618698e3783669e7c581",
"assets/icons/gift.webp": "f29ebf5e3aea9051a18049a5b0372e45",
"assets/icons/exit2.webp": "38d5c1c37d68072845c0070b83a15687",
"assets/icons/settings.webp": "497c5270161d0c259c40f3db24a23e49",
"assets/icons/lock.webp": "b4258a96d1d601718fb3a0895bcf831e",
"assets/icons/sound_off.webp": "9f94cbccc36e6a066b78332f61cb2a1b",
"assets/icons/tg.webp": "b244a357bdb75e0978f42e8f44801304",
"assets/icons/chat.webp": "31375fca81fa9fc16cb98ae3b1c7b608",
"assets/icons/exit.webp": "23851a717f974d8869ca7286cf603198",
"assets/icons/ad.webp": "c18fcd1ca174d305a663122274e77541",
"assets/icons/clock.webp": "3f56d2f02c97218b436f09b0114745b2",
"assets/icons/exit3.webp": "93390eac54377fe84f8e72cd1ba9c86f",
"assets/icons/down.webp": "a03694c11d0c594c407cfd07a917e6ea",
"assets/packages/cupertino_icons/assets/CupertinoIcons.ttf": "3c81d572636c9fdf59d16a1b2a926270",
"assets/shaders/ink_sparkle.frag": "ecc85a2e95f5e9f53123dcaf8cb9b6ce",
"assets/AssetManifest.bin": "fb1b522630fd39c059eeec2f4cfa89da",
"assets/fonts/MaterialIcons-Regular.otf": "8419d0bcce9edacabc13177aa440e80e",
"assets/AssetManifest.bin": "999738b9fec34c457d537edf879cbd21",
"assets/fonts/MaterialIcons-Regular.otf": "f88dfb9ff0e5a2bc4e1872513ff14262",
"canvaskit/skwasm.js": "1ef3ea3a0fec4569e5d531da25f34095",
"canvaskit/skwasm_heavy.js": "413f5b2b2d9345f37de148e2544f584f",
"canvaskit/skwasm.js.symbols": "0088242d10d7e7d6d2649d1fe1bda7c1",

View file

@ -1,67 +1,235 @@
// Initialize Adsgram with the configured block ID
const AdController = window.Adsgram.init({ blockId: "16505" });
// Adsgram SDK integration for rewarded ads
// This file provides a bridge between Dart code and the Adsgram JavaScript SDK
// Store reward callback function
// Wait for SDK to load if it's not immediately available
(function() {
let sdkCheckAttempts = 0;
const maxCheckAttempts = 50; // Check for up to 5 seconds (50 * 100ms)
function checkSDK() {
if (typeof window.Adsgram !== 'undefined') {
console.log('Adsgram SDK loaded successfully');
return;
}
sdkCheckAttempts++;
if (sdkCheckAttempts < maxCheckAttempts) {
setTimeout(checkSDK, 100);
} else {
console.warn('Adsgram SDK not loaded after timeout. Make sure sad.min.js is included in index.html');
}
}
// Start checking after a short delay to allow script to load
setTimeout(checkSDK, 100);
})();
// Store callbacks for ad lifecycle events
let rewardCallback = null;
let errorCallback = null;
let currentAdController = null;
// Function to show ad (called from Dart)
// Default block ID (can be overridden)
const DEFAULT_BLOCK_ID = "16505";
/**
* Initialize Adsgram ad controller with a specific block ID
* @param {string} blockId - The Adsgram block ID
* @returns {object} Adsgram ad controller instance
* @throws {Error} If SDK is not available or initialization fails
*/
function initAdController(blockId) {
if (typeof window.Adsgram === 'undefined') {
const error = 'Adsgram SDK not available. Make sure sad.min.js is loaded.';
console.error(error);
throw new Error(error);
}
if (!blockId || typeof blockId !== 'string' || blockId.trim() === '') {
const error = 'Invalid block ID provided';
console.error(error);
throw new Error(error);
}
try {
const controller = window.Adsgram.init({ blockId: blockId });
if (!controller) {
throw new Error('Failed to initialize Adsgram controller: init returned null');
}
return controller;
} catch (error) {
const errorMessage = error?.message || error?.toString() || 'Unknown initialization error';
console.error('Failed to initialize Adsgram controller:', errorMessage);
throw new Error(`Failed to initialize Adsgram controller: ${errorMessage}`);
}
}
/**
* Show a rewarded ad using the default block ID
* Called from Dart code via js_util.callMethod
* @returns {Promise} Promise that resolves when ad completes or rejects on error
*/
function showAd() {
return AdController.show().then((result) => {
// user watch ad till the end or close it in interstitial format
// your code to reward user for rewarded format
return showAdWithBlockId(DEFAULT_BLOCK_ID);
}
/**
* Show a rewarded ad with a specific block ID
* Called from Dart code via js_util.callMethod
* @param {string} blockId - The Adsgram block ID to use
* @returns {Promise} Promise that resolves when ad completes or rejects on error
*/
function showAdWithBlockId(blockId) {
// Validate block ID
if (!blockId || typeof blockId !== 'string' || blockId.trim() === '') {
const error = 'Invalid block ID provided';
console.error(error);
if (errorCallback && typeof errorCallback === 'function') {
try {
errorCallback(error);
} catch (callbackError) {
console.error('Error in error callback:', callbackError);
}
}
return Promise.reject(new Error(error));
}
// Check SDK availability
if (typeof window.Adsgram === 'undefined') {
const error = 'Adsgram SDK not available. Make sure sad.min.js is loaded.';
console.error(error);
if (errorCallback && typeof errorCallback === 'function') {
try {
errorCallback(error);
} catch (callbackError) {
console.error('Error in error callback:', callbackError);
}
}
return Promise.reject(new Error(error));
}
try {
// Initialize controller for this specific block
currentAdController = initAdController(blockId);
if (!currentAdController || typeof currentAdController.show !== 'function') {
const error = 'Invalid ad controller: show method not available';
console.error(error);
if (errorCallback && typeof errorCallback === 'function') {
try {
errorCallback(error);
} catch (callbackError) {
console.error('Error in error callback:', callbackError);
}
}
return Promise.reject(new Error(error));
}
// Show the ad
return currentAdController.show().then((result) => {
// Ad completed successfully - user watched till the end
console.log('Ad completed successfully', result);
// Call reward callback if set
if (window.rewardCallback && typeof window.rewardCallback === 'function') {
window.rewardCallback();
if (rewardCallback && typeof rewardCallback === 'function') {
try {
rewardCallback();
} catch (callbackError) {
console.error('Error in reward callback:', callbackError);
// Don't fail the promise if callback has an error
}
}
return result;
}).catch((result) => {
// user get error during playing ad
// do nothing or whatever you want
console.error('Ad failed', result);
}).catch((error) => {
// Ad failed or was closed early
console.error('Ad failed or was closed:', error);
// Format error message
let errorMessage = 'Ad failed';
if (error) {
if (typeof error === 'object') {
if (error.message) {
errorMessage = error.message;
} else {
try {
errorMessage = JSON.stringify(error);
} catch (e) {
errorMessage = error.toString();
}
}
} else {
errorMessage = error.toString();
}
}
// Call error callback if set
if (window.errorCallback && typeof window.errorCallback === 'function') {
window.errorCallback(JSON.stringify(result, null, 4));
if (errorCallback && typeof errorCallback === 'function') {
try {
errorCallback(errorMessage);
} catch (callbackError) {
console.error('Error in error callback:', callbackError);
}
}
throw result;
throw error;
});
} catch (error) {
// Initialization or show() call failed
let errorMessage = 'Failed to show ad';
if (error) {
if (error.message) {
errorMessage = error.message;
} else {
errorMessage = error.toString();
}
}
console.error('Failed to show ad:', errorMessage);
if (errorCallback && typeof errorCallback === 'function') {
try {
errorCallback(errorMessage);
} catch (callbackError) {
console.error('Error in error callback:', callbackError);
}
}
// Function to show ad with specific block ID
function showAdWithBlockId(blockId) {
const dynamicController = window.Adsgram.init({ blockId: blockId });
return dynamicController.show().then((result) => {
console.log('Ad completed successfully for block', blockId, result);
// Call reward callback if set
if (window.rewardCallback && typeof window.rewardCallback === 'function') {
window.rewardCallback();
return Promise.reject(error);
}
}
return result;
}).catch((result) => {
console.error('Ad failed for block', blockId, result);
// Call error callback if set
if (window.errorCallback && typeof window.errorCallback === 'function') {
window.errorCallback(JSON.stringify(result, null, 4));
}
throw result;
});
}
// Function to set reward callback from Dart
/**
* Set the reward callback function
* Called from Dart code to register callback for successful ad completion
* @param {Function} callback - Function to call when ad completes successfully
*/
function setRewardCallback(callback) {
window.rewardCallback = callback;
if (callback && typeof callback === 'function') {
rewardCallback = callback;
console.log('Reward callback registered');
} else {
console.warn('Invalid reward callback provided');
rewardCallback = null;
}
}
// Function to set error callback from Dart
/**
* Set the error callback function
* Called from Dart code to register callback for ad errors
* @param {Function} callback - Function to call when ad fails (takes error message as parameter)
*/
function setErrorCallback(callback) {
window.errorCallback = callback;
if (callback && typeof callback === 'function') {
errorCallback = callback;
console.log('Error callback registered');
} else {
console.warn('Invalid error callback provided');
errorCallback = null;
}
}
// Make functions available globally for Dart interop
window.showAd = showAd;
window.showAdWithBlockId = showAdWithBlockId;
window.setRewardCallback = setRewardCallback;
window.setErrorCallback = setErrorCallback;

File diff suppressed because one or more lines are too long

View file

@ -1,4 +1,3 @@
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:yx_scope/yx_scope.dart';
import '../../../domain/services/http_repository_v2.dart';
@ -11,13 +10,6 @@ import '../app_scope_container.dart';
class StorageModule extends ScopeModule<AppScopeContainer> {
StorageModule(super.container);
// SecureStorage for tokens (legacy, kept for compatibility)
late final secureStorageDep = dep(
() => const FlutterSecureStorage(
aOptions: AndroidOptions(encryptedSharedPreferences: true),
),
);
// HTTP Repository V2 (primary - for web app)
late final httpRepositoryV2Dep = dep(
() => HttpRepositoryV2.withDefaults(container.sharedPreferences),
@ -28,8 +20,6 @@ class StorageModule extends ScopeModule<AppScopeContainer> {
() => ThemeStateManager(container.sharedPreferences),
);
FlutterSecureStorage get secureStorage => secureStorageDep.get;
/// Primary HTTP repository - uses API v2
HttpRepositoryV2 get httpRepository => httpRepositoryV2Dep.get;

View file

@ -534,7 +534,7 @@ packages:
source: hosted
version: "5.9.3"
flutter_secure_storage:
dependency: "direct main"
dependency: transitive
description:
name: flutter_secure_storage
sha256: "9cad52d75ebc511adfae3d447d5d13da15a55a92c9410e50f67335b6d21d16ea"

View file

@ -56,7 +56,6 @@ dependencies:
# Storage
shared_preferences: ^2.2.3
flutter_secure_storage: ^9.2.2
# UI
flutter_screenutil: ^5.9.0

View file

@ -196,6 +196,16 @@ else
exit 1
fi
# Deploy updated nginx configurations for all services
echo "🔧 Deploying updated nginx configurations..."
cd "$SCRIPT_DIR"
./generate-nginx-configs.sh
./deploy-nginx-configs.sh
# Final nginx check
echo "🔍 Final nginx status check..."
./check-nginx.sh
sudo systemctl daemon-reload
sudo systemctl start mnemo_cards_server
journalctl -u mnemo_cards_server --since "1min ago"

View file

@ -1,19 +1,19 @@
#!/bin/bash
# Script to check and reload nginx
# Script to check and fix nginx configuration and service
set -e
SERVER_IP="147.45.152.129"
SERVER_USER="root"
echo "🔧 Checking and reloading nginx..."
echo "🔧 Checking and fixing nginx..."
echo ""
ssh "$SERVER_USER@$SERVER_IP" << 'ENDSSH'
set -e
echo "📊 Checking nginx status..."
echo "📊 Checking nginx installation..."
if /usr/sbin/nginx -v 2>&1; then
echo "✅ nginx is installed"
else
@ -21,60 +21,166 @@ ssh "$SERVER_USER@$SERVER_IP" << 'ENDSSH'
exit 1
fi
echo ""
echo "🧹 Checking for port conflicts..."
# Check if ports 80 and 443 are free
if ss -tlnp | grep -q ":80 "; then
echo "⚠️ Port 80 is already in use:"
ss -tlnp | grep ":80 "
echo "Attempting to free port 80..."
# Kill processes listening on port 80 (except nginx)
ss -tlnp | grep ":80 " | grep -v nginx | awk '{print $6}' | cut -d'=' -f2 | cut -d',' -f1 | xargs -r kill -9 2>/dev/null || true
sleep 1
fi
if ss -tlnp | grep -q ":443 "; then
echo "⚠️ Port 443 is already in use:"
ss -tlnp | grep ":443 "
echo "Attempting to free port 443..."
# Kill processes listening on port 443 (except nginx)
ss -tlnp | grep ":443 " | grep -v nginx | awk '{print $6}' | cut -d'=' -f2 | cut -d',' -f1 | xargs -r kill -9 2>/dev/null || true
sleep 1
fi
echo ""
echo "🧹 Checking for orphaned nginx processes..."
NGINX_PIDS=$(pgrep -f nginx | wc -l)
if [ "$NGINX_PIDS" -gt 0 ]; then
echo "⚠️ Found $NGINX_PIDS nginx processes running"
if ! systemctl is-active --quiet nginx; then
echo "Systemd thinks nginx is stopped, but processes exist. Cleaning up..."
pkill -9 nginx
sleep 2
fi
fi
echo ""
echo "📊 Testing nginx configuration..."
/usr/sbin/nginx -t
if /usr/sbin/nginx -t; then
echo "✅ Configuration syntax is valid"
else
echo "❌ Configuration syntax error!"
echo "Checking for common issues..."
# Check for missing events block
if ! grep -q "events {" /etc/nginx/nginx.conf; then
echo "⚠️ Missing events block in nginx.conf"
fi
# Check for server blocks outside http
if grep -q "^server {" /etc/nginx/nginx.conf; then
echo "⚠️ Found server block outside http context in nginx.conf"
fi
# Check for duplicate directives
DUPLICATE_USER=$(grep "^user " /etc/nginx/nginx.conf | wc -l)
if [ "$DUPLICATE_USER" -gt 1 ]; then
echo "⚠️ Found $DUPLICATE_USER 'user' directives (should be 1)"
fi
echo "Please fix the configuration errors and try again"
exit 1
fi
echo ""
echo "📊 Checking nginx service status..."
systemctl status nginx --no-pager || true
if systemctl is-active --quiet nginx; then
echo "✅ nginx service is running"
systemctl status nginx --no-pager | head -5
echo ""
echo "🔄 Reloading nginx..."
systemctl reload nginx
echo "✅ nginx reloaded"
echo "🔄 Reloading nginx configuration..."
if systemctl reload nginx; then
echo "✅ nginx reloaded successfully"
else
echo "❌ nginx reload failed, restarting..."
systemctl restart nginx
fi
else
echo "⚠️ nginx service is not running, starting..."
if systemctl start nginx; then
echo "✅ nginx started successfully"
else
echo "❌ nginx failed to start!"
systemctl status nginx --no-pager
exit 1
fi
fi
echo ""
echo "📊 Checking if nginx is running..."
systemctl is-active nginx && echo "✅ nginx is active" || echo "❌ nginx is not active"
echo "📊 Checking if nginx is active..."
if systemctl is-active --quiet nginx; then
echo "✅ nginx is active"
else
echo "❌ nginx is not active"
exit 1
fi
echo ""
echo "📊 Checking vscode.mnemo-cards.online nginx config..."
if [ -f /etc/nginx/sites-enabled/vscode.mnemo-cards.online ]; then
echo "✅ vscode nginx config is enabled"
elif [ -f /etc/nginx/sites-available/vscode.mnemo-cards.online ]; then
echo "⚠️ vscode nginx config exists but not enabled"
echo "Enabling it..."
ln -sf /etc/nginx/sites-available/vscode.mnemo-cards.online /etc/nginx/sites-enabled/
echo "📊 Checking site configurations..."
# Check all expected configurations
SITES=("vscode.mnemo-cards.online" "code.mnemo-cards.online" "mnemo-cards.online")
for site in "${SITES[@]}"; do
if [ -f "/etc/nginx/sites-enabled/$site" ]; then
echo "$site config is enabled"
elif [ -f "/etc/nginx/sites-available/$site" ]; then
echo "⚠️ $site config exists but not enabled"
echo "Enabling $site..."
ln -sf "/etc/nginx/sites-available/$site" "/etc/nginx/sites-enabled/"
/usr/sbin/nginx -t && systemctl reload nginx
echo "✅ Enabled and reloaded"
echo "$site enabled and reloaded"
else
echo "❌ vscode nginx config not found"
echo "$site config not found"
fi
done
echo ""
echo "📊 Checking SSL certificates..."
if [ -d "/etc/letsencrypt/live/mnemo-cards.online" ]; then
echo "✅ Let's Encrypt certificates found for mnemo-cards.online"
# Ensure correct permissions
chmod 755 /etc/letsencrypt/archive 2>/dev/null || true
chmod 755 /etc/letsencrypt/live 2>/dev/null || true
find /etc/letsencrypt -type d -exec chmod 755 {} \; 2>/dev/null || true
find /etc/letsencrypt -type f -exec chmod 644 {} \; 2>/dev/null || true
chmod 600 /etc/letsencrypt/archive/mnemo-cards.online/privkey*.pem 2>/dev/null || true
chmod 600 /etc/letsencrypt/live/mnemo-cards.online/privkey.pem 2>/dev/null || true
else
echo "⚠️ Let's Encrypt certificates not found"
fi
echo ""
echo "📊 Checking code.mnemo-cards.online DNS resolution..."
if host code.mnemo-cards.online &> /dev/null; then
host code.mnemo-cards.online
echo "📊 Checking DNS resolution..."
DOMAINS=("code.mnemo-cards.online" "vscode.mnemo-cards.online" "mnemo-cards.online")
for domain in "${DOMAINS[@]}"; do
if host "$domain" &> /dev/null; then
echo "$domain resolves correctly"
else
echo "⚠️ DNS resolution failed for code.mnemo-cards.online"
echo "⚠️ DNS resolution failed for $domain"
fi
done
if host vscode.mnemo-cards.online &> /dev/null; then
host vscode.mnemo-cards.online
echo ""
echo "📊 Testing HTTP responses..."
for domain in "${DOMAINS[@]}"; do
if curl -I -k "https://$domain/" --max-time 10 --silent | head -1 | grep -q "200\|301\|302"; then
echo "$domain responds correctly"
else
echo "⚠️ DNS resolution failed for vscode.mnemo-cards.online"
echo "⚠️ $domain not responding or returning error"
fi
done
ENDSSH
echo ""
echo "✅ Done!"
echo ""
echo "🌐 Now test these URLs in your browser:"
echo " https://vscode.mnemo-cards.online/"
echo " https://code.mnemo-cards.online/"
echo "🌐 Test these URLs in your browser:"
echo " https://mnemo-cards.online/ (main web app)"
echo " https://code.mnemo-cards.online/ (Forgejo)"
echo " https://vscode.mnemo-cards.online/ (VSCode Server)"
echo ""
echo "🔑 Password: AGktOidxrah1KVC0"

View file

@ -0,0 +1,140 @@
#!/bin/bash
# Script to deploy generated nginx configurations to the server
# This script uses the configurations generated by generate-nginx-configs.sh
set -e
SERVER_IP="147.45.152.129"
SERVER_USER="root"
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
CONFIG_DIR="$SCRIPT_DIR/generated_configs"
echo "🚀 Deploying nginx configurations to server..."
echo "Server: $SERVER_IP"
echo "Config directory: $CONFIG_DIR"
echo ""
# Check if config directory exists and has files
if [ ! -d "$CONFIG_DIR" ]; then
echo "❌ Config directory not found: $CONFIG_DIR"
echo "Run generate-nginx-configs.sh first"
exit 1
fi
if [ ! "$(ls -A "$CONFIG_DIR"/*.conf 2>/dev/null)" ]; then
echo "❌ No configuration files found in $CONFIG_DIR"
echo "Run generate-nginx-configs.sh first"
exit 1
fi
echo "📤 Uploading configurations..."
scp "$CONFIG_DIR"/*.conf "$SERVER_USER@$SERVER_IP:/tmp/"
echo ""
echo "⚙️ Installing configurations on server..."
ssh "$SERVER_USER@$SERVER_IP" << 'ENDSSH'
set -e
echo "🛑 Stopping nginx service..."
systemctl stop nginx || true
echo "🧹 Cleaning up old configurations..."
rm -f /etc/nginx/sites-enabled/vscode*
rm -f /etc/nginx/sites-enabled/forgejo*
rm -f /etc/nginx/sites-enabled/code*
rm -f /etc/nginx/sites-enabled/mnemo*
echo "🧹 Killing any orphaned nginx processes..."
pkill -9 nginx 2>/dev/null || true
sleep 2
echo "📋 Installing new configurations..."
mv /tmp/vscode.mnemo-cards.online.conf /etc/nginx/sites-available/ 2>/dev/null || true
mv /tmp/code.mnemo-cards.online.conf /etc/nginx/sites-available/ 2>/dev/null || true
mv /tmp/mnemo-cards.online.conf /etc/nginx/sites-available/ 2>/dev/null || true
echo "🔗 Creating symbolic links..."
ln -sf /etc/nginx/sites-available/vscode.mnemo-cards.online.conf /etc/nginx/sites-enabled/ 2>/dev/null || true
ln -sf /etc/nginx/sites-available/code.mnemo-cards.online.conf /etc/nginx/sites-enabled/ 2>/dev/null || true
ln -sf /etc/nginx/sites-available/mnemo-cards.online.conf /etc/nginx/sites-enabled/ 2>/dev/null || true
echo "✅ Configurations installed"
echo ""
echo "📊 Validating configuration..."
if /usr/sbin/nginx -t; then
echo "✅ Configuration syntax is valid"
else
echo "❌ Configuration syntax error!"
echo "Checking configuration files..."
for config in /etc/nginx/sites-enabled/*; do
echo "Testing $(basename "$config")..."
if ! /usr/sbin/nginx -t -c /etc/nginx/nginx.conf 2>&1 | grep -q "$(basename "$config")"; then
echo " ❌ Error in $(basename "$config")"
head -20 "$config"
fi
done
echo "Restoring basic configuration..."
# Create minimal working config
cat > /etc/nginx/sites-available/emergency.conf << 'EMERGENCY'
server {
listen 80;
server_name _;
return 200 "nginx emergency mode\n";
access_log off;
}
EMERGENCY
ln -sf /etc/nginx/sites-available/emergency.conf /etc/nginx/sites-enabled/
systemctl start nginx
echo "Emergency configuration activated"
exit 1
fi
echo ""
echo "🔄 Starting nginx service..."
if systemctl start nginx; then
echo "✅ nginx started successfully"
else
echo "❌ nginx failed to start!"
systemctl status nginx --no-pager
exit 1
fi
echo ""
echo "📊 Verifying services are accessible..."
# Test each service
SERVICES=(
"mnemo-cards.online:80:301" # Should redirect to HTTPS
"code.mnemo-cards.online:80:301"
"vscode.mnemo-cards.online:80:301"
)
for service in "${SERVICES[@]}"; do
DOMAIN=$(echo "$service" | cut -d: -f1)
PORT=$(echo "$service" | cut -d: -f2)
EXPECTED_CODE=$(echo "$service" | cut -d: -f3)
if curl -I --max-time 5 "http://$DOMAIN:$PORT/" 2>/dev/null | grep -q "HTTP/1.1 $EXPECTED_CODE"; then
echo "$DOMAIN redirects correctly"
else
echo "⚠️ $DOMAIN not responding as expected"
fi
done
ENDSSH
echo ""
echo "✅ Deployment completed!"
echo ""
echo "🌐 Services should be accessible at:"
echo " https://mnemo-cards.online/ (main web app)"
echo " https://code.mnemo-cards.online/ (Forgejo)"
echo " https://vscode.mnemo-cards.online/ (VSCode Server)"
echo ""
echo "🔧 To check status: ./check-nginx.sh"
echo "🔧 To regenerate configs: ./generate-nginx-configs.sh"

View file

@ -8,80 +8,297 @@ SERVER_IP="147.45.152.129"
SERVER_USER="root"
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
VSCODE_NGINX_CONF="$PROJECT_ROOT/mnemo_cards_web_v2/deploy/vscode-nginx.conf"
echo "🚀 Deploying VSCode nginx configuration..."
echo "🚀 Deploying nginx configurations to server..."
echo "Server: $SERVER_IP"
echo "Config file: $VSCODE_NGINX_CONF"
echo ""
# Check if config file exists
if [ ! -f "$VSCODE_NGINX_CONF" ]; then
echo "❌ Config file not found: $VSCODE_NGINX_CONF"
exit 1
# Function to generate nginx configuration for a service
generate_nginx_config() {
local domain=$1
local upstream_port=$2
local service_name=$3
local rate_limit_zone=$4
local rate_limit_burst=$5
cat << EOF
# Nginx configuration for $domain
# Generated by deploy script
server {
listen 80;
server_name $domain;
return 301 https://\$server_name\$request_uri;
}
server {
listen 443 ssl http2;
server_name $domain;
# SSL configuration
ssl_certificate /etc/letsencrypt/live/mnemo-cards.online/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mnemo-cards.online/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# Client settings
client_max_body_size 100M;
EOF
# Add rate limiting for VSCode
if [ "$service_name" = "vscode" ]; then
cat << EOF
# Rate limiting for VSCode
limit_req zone=$rate_limit_zone burst=$rate_limit_burst nodelay;
# Static files - no rate limiting
location ~ ^/(static|out|node_modules)/ {
proxy_pass http://127.0.0.1:$upstream_port;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
proxy_cache_valid 200 1h;
add_header Cache-Control "public, max-age=3600";
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# Static file extensions - no rate limiting
location ~ \.(js|css|woff|woff2|ttf|eot|png|jpg|jpeg|gif|svg|ico|webp|map|json)$ {
proxy_pass http://127.0.0.1:$upstream_port;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
proxy_cache_valid 200 1h;
add_header Cache-Control "public, max-age=3600";
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# Special rate limiting for login attempts
location /login {
limit_req zone=vscode_login burst=2 nodelay;
proxy_pass http://127.0.0.1:$upstream_port;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection "upgrade";
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
proxy_buffering off;
proxy_request_buffering off;
}
EOF
fi
echo "📤 Uploading nginx configuration..."
scp "$VSCODE_NGINX_CONF" "$SERVER_USER@$SERVER_IP:/tmp/vscode-nginx.conf"
# Main proxy location
cat << EOF
# Proxy to $service_name
EOF
if [ "$service_name" = "vscode" ]; then
cat << EOF
location / {
limit_req zone=$rate_limit_zone burst=$rate_limit_burst nodelay;
EOF
else
cat << EOF
location / {
EOF
fi
cat << EOF
proxy_pass http://127.0.0.1:$upstream_port;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection "upgrade";
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
proxy_buffering off;
proxy_request_buffering off;
}
# Gzip compression
echo " gzip on;"
echo " gzip_vary on;"
echo " gzip_min_length 1024;"
echo " gzip_proxied expired no-cache no-store private auth;"
echo " gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/javascript;"
echo ""
echo " # Security - deny access to hidden files"
echo " location ~ /\. {"
echo " deny all;"
echo " }"
echo "}"
}
# Generate configurations for all services
echo "📝 Generating nginx configurations..."
# VSCode configuration
generate_nginx_config "vscode.mnemo-cards.online" "8443" "vscode" "vscode_general" "50" > /tmp/vscode-nginx.conf
# Forgejo configuration
generate_nginx_config "code.mnemo-cards.online" "3000" "forgejo" "" "" > /tmp/forgejo-nginx.conf
# Main web app configuration
cat > /tmp/mnemo_cards_main-nginx.conf << 'EOF'
server {
listen 80;
server_name mnemo-cards.online www.mnemo-cards.online;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name mnemo-cards.online www.mnemo-cards.online;
ssl_certificate /etc/letsencrypt/live/mnemo-cards.online/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mnemo-cards.online/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
# Client settings
client_max_body_size 1000M;
root /var/www/mnemo_cards;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# ACME challenge for certificate renewal
location /.well-known/acme-challenge/ {
root /var/www/html;
try_files $uri =404;
}
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/javascript;
# Security - deny access to hidden files
location ~ /\. {
deny all;
}
}
EOF
echo "📤 Uploading configurations to server..."
scp /tmp/vscode-nginx.conf "$SERVER_USER@$SERVER_IP:/tmp/"
scp /tmp/forgejo-nginx.conf "$SERVER_USER@$SERVER_IP:/tmp/"
scp /tmp/mnemo_cards_main-nginx.conf "$SERVER_USER@$SERVER_IP:/tmp/"
echo ""
echo "⚙️ Installing configuration on server..."
echo "⚙️ Installing configurations on server..."
ssh "$SERVER_USER@$SERVER_IP" << 'ENDSSH'
set -e
echo "📋 Moving config to nginx sites-available..."
echo "🛑 Stopping nginx service..."
systemctl stop nginx
echo "🧹 Cleaning up old configurations..."
rm -f /etc/nginx/sites-enabled/vscode*
rm -f /etc/nginx/sites-enabled/forgejo*
rm -f /etc/nginx/sites-enabled/mnemo_cards*
echo "📋 Installing new configurations..."
mv /tmp/vscode-nginx.conf /etc/nginx/sites-available/vscode.mnemo-cards.online
mv /tmp/forgejo-nginx.conf /etc/nginx/sites-available/code.mnemo-cards.online
mv /tmp/mnemo_cards_main-nginx.conf /etc/nginx/sites-available/mnemo-cards.online
echo "🔗 Creating symbolic link in sites-enabled..."
ln -sf /etc/nginx/sites-available/vscode.mnemo-cards.online /etc/nginx/sites-enabled/vscode.mnemo-cards.online
echo "🔗 Creating symbolic links..."
ln -sf /etc/nginx/sites-available/vscode.mnemo-cards.online /etc/nginx/sites-enabled/
ln -sf /etc/nginx/sites-available/code.mnemo-cards.online /etc/nginx/sites-enabled/
ln -sf /etc/nginx/sites-available/mnemo-cards.online /etc/nginx/sites-enabled/
echo "✅ Configuration installed"
echo "✅ Configurations installed"
echo ""
echo "📊 Checking nginx configuration syntax..."
/usr/sbin/nginx -t
echo ""
echo "🔄 Reloading nginx..."
systemctl reload nginx
echo "✅ nginx reloaded successfully"
echo ""
echo "📊 Checking SSL certificates..."
if [ -d "/etc/letsencrypt/live/vscode.mnemo-cards.online" ]; then
echo "✅ Let's Encrypt certificates found"
ls -la /etc/letsencrypt/live/vscode.mnemo-cards.online/
if /usr/sbin/nginx -t; then
echo "✅ Configuration syntax is valid"
else
echo "⚠️ Let's Encrypt certificates not found"
echo "You may need to obtain certificates with:"
echo " sudo certbot certonly --standalone -d vscode.mnemo-cards.online"
echo "❌ Configuration syntax error!"
echo "Restoring previous configurations..."
systemctl start nginx
exit 1
fi
echo ""
echo "📊 Checking rate limiting zones in main nginx config..."
if grep -q "vscode_general" /etc/nginx/nginx.conf; then
echo "✅ Rate limiting zones are configured"
echo "🔄 Starting nginx service..."
systemctl start nginx
if systemctl is-active --quiet nginx; then
echo "✅ nginx started successfully"
else
echo "⚠️ Rate limiting zones not found in main config"
echo "Adding rate limiting zones..."
# Backup current config
cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.backup
# Add rate limiting zones in http block
sed -i '/http {/a\ # Rate limiting zones for VSCode Server\n limit_req_zone $binary_remote_addr zone=vscode_general:10m rate=100r/m;\n limit_req_zone $binary_remote_addr zone=vscode_login:10m rate=5r/m;' /etc/nginx/nginx.conf
echo "✅ Rate limiting zones added"
echo "❌ nginx failed to start!"
systemctl status nginx --no-pager
exit 1
fi
echo ""
echo "📊 Testing updated configuration..."
/usr/sbin/nginx -t
echo ""
echo "🔄 Reloading nginx again..."
systemctl reload nginx
echo "📊 Checking SSL certificates..."
if [ -d "/etc/letsencrypt/live/mnemo-cards.online" ]; then
echo "✅ Let's Encrypt certificates found"
# Ensure correct permissions
chmod 755 /etc/letsencrypt/archive
chmod 755 /etc/letsencrypt/live
find /etc/letsencrypt -type d -exec chmod 755 {} \;
find /etc/letsencrypt -type f -exec chmod 644 {} \;
chmod 600 /etc/letsencrypt/archive/mnemo-cards.online/privkey*.pem
chmod 600 /etc/letsencrypt/live/mnemo-cards.online/privkey.pem
else
echo "⚠️ Let's Encrypt certificates not found"
fi
ENDSSH
@ -89,13 +306,10 @@ ENDSSH
echo ""
echo "✅ Done!"
echo ""
echo "🌐 VSCode Server should now be accessible at:"
echo " https://vscode.mnemo-cards.online/"
echo " https://code.mnemo-cards.online/ (same service)"
echo "🌐 Services should now be accessible at:"
echo " https://mnemo-cards.online/ (main web app)"
echo " https://code.mnemo-cards.online/ (Forgejo)"
echo " https://vscode.mnemo-cards.online/ (VSCode Server)"
echo ""
echo "🔑 Password: AGktOidxrah1KVC0"
echo ""
echo "📝 If you get SSL errors, you may need to obtain certificates:"
echo " ssh root@$SERVER_IP"
echo " sudo certbot certonly --standalone -d vscode.mnemo-cards.online -d code.mnemo-cards.online"
echo "🔑 VSCode Password: AGktOidxrah1KVC0"

View file

@ -0,0 +1,245 @@
#!/bin/bash
# Script to generate all nginx configurations for the project
# This ensures consistent configuration across all deployments
set -e
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
OUTPUT_DIR="$SCRIPT_DIR/generated_configs"
echo "🔧 Generating nginx configurations..."
echo "Output directory: $OUTPUT_DIR"
echo ""
# Create output directory
mkdir -p "$OUTPUT_DIR"
# Function to generate nginx configuration for a service
generate_nginx_config() {
local domain=$1
local upstream_port=$2
local service_name=$3
local rate_limit_zone=$4
local rate_limit_burst=$5
local output_file="$OUTPUT_DIR/$domain.conf"
echo "📝 Generating config for $domain (port $upstream_port)..."
cat > "$output_file" << EOF
# Nginx configuration for $domain
# Generated by generate-nginx-configs.sh on $(date)
# Service: $service_name
server {
listen 80;
server_name $domain;
return 301 https://\$server_name\$request_uri;
}
server {
listen 443 ssl http2;
server_name $domain;
# SSL configuration
ssl_certificate /etc/letsencrypt/live/mnemo-cards.online/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mnemo-cards.online/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
EOF
# Add service-specific settings
case $service_name in
vscode)
cat >> "$output_file" << EOF
# VSCode Server specific settings
client_max_body_size 100M;
# Rate limiting for VSCode
limit_req_zone \$binary_remote_addr zone=vscode_general:10m rate=100r/m;
limit_req_zone \$binary_remote_addr zone=vscode_login:10m rate=5r/m;
# Static files - no rate limiting
location ~ ^/(static|out|node_modules)/ {
proxy_pass http://127.0.0.1:$upstream_port;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
proxy_cache_valid 200 1h;
add_header Cache-Control "public, max-age=3600";
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# Static file extensions - no rate limiting
location ~ \.(js|css|woff|woff2|ttf|eot|png|jpg|jpeg|gif|svg|ico|webp|map|json)$ {
proxy_pass http://127.0.0.1:$upstream_port;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
proxy_cache_valid 200 1h;
add_header Cache-Control "public, max-age=3600";
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# Special rate limiting for login attempts
location /login {
limit_req zone=vscode_login burst=2 nodelay;
proxy_pass http://127.0.0.1:$upstream_port;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection "upgrade";
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
proxy_buffering off;
proxy_request_buffering off;
}
# Main proxy location with rate limiting
location / {
limit_req zone=vscode_general burst=50 nodelay;
EOF
;;
forgejo)
cat >> "$output_file" << EOF
# Forgejo (Git) specific settings
client_max_body_size 100M;
# Main proxy location
location / {
proxy_pass http://127.0.0.1:$upstream_port;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection "upgrade";
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
proxy_buffering off;
proxy_request_buffering off;
}
EOF
;;
webapp)
cat >> "$output_file" << EOF
# Web app specific settings
client_max_body_size 1000M;
root /var/www/mnemo_cards;
index index.html;
location / {
try_files \$uri \$uri/ /index.html;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# ACME challenge for certificate renewal
location /.well-known/acme-challenge/ {
root /var/www/html;
try_files \$uri =404;
}
EOF
;;
esac
# Common proxy settings for services that don't have custom proxy blocks
if [ "$service_name" = "vscode" ]; then
cat >> "$output_file" << EOF
proxy_pass http://127.0.0.1:$upstream_port;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection "upgrade";
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
proxy_buffering off;
proxy_request_buffering off;
}
EOF
fi
# Gzip compression for all services
cat >> "$output_file" << EOF
}
# Gzip compression (configured globally in nginx.conf)
# Security - deny access to hidden files
location ~ /\. {
deny all;
}
}
EOF
echo "✅ Generated $output_file"
}
# Generate configurations for all services
# Main web application
generate_nginx_config "mnemo-cards.online" "" "webapp"
# VSCode Server
generate_nginx_config "vscode.mnemo-cards.online" "8443" "vscode"
# Forgejo (Git server)
generate_nginx_config "code.mnemo-cards.online" "3000" "forgejo"
echo ""
echo "📊 Validating generated configurations..."
# Test each configuration
for config in "$OUTPUT_DIR"/*.conf; do
echo "Testing $(basename "$config")..."
/usr/sbin/nginx -t -c /etc/nginx/nginx.conf -g "include $config;" 2>/dev/null && echo "✅ Valid" || echo "❌ Invalid"
done
echo ""
echo "✅ All configurations generated successfully!"
echo ""
echo "📁 Generated files in: $OUTPUT_DIR"
echo ""
echo "🚀 Use deploy-nginx-configs.sh to deploy these configurations to the server"

View file

@ -0,0 +1,63 @@
# Nginx configuration for code.mnemo-cards.online
# Generated by generate-nginx-configs.sh on Sun Nov 23 02:51:54 MSK 2025
# Service: forgejo
server {
listen 80;
server_name code.mnemo-cards.online;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name code.mnemo-cards.online;
# SSL configuration
ssl_certificate /etc/letsencrypt/live/mnemo-cards.online/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mnemo-cards.online/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# Forgejo (Git) specific settings
client_max_body_size 100M;
# Main proxy location
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
proxy_buffering off;
proxy_request_buffering off;
}
}
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/javascript;
# Security - deny access to hidden files
location ~ /\. {
deny all;
}
}

View file

@ -0,0 +1,61 @@
# Nginx configuration for mnemo-cards.online
# Generated by generate-nginx-configs.sh on Sun Nov 23 02:51:54 MSK 2025
# Service: webapp
server {
listen 80;
server_name mnemo-cards.online;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name mnemo-cards.online;
# SSL configuration
ssl_certificate /etc/letsencrypt/live/mnemo-cards.online/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mnemo-cards.online/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# Web app specific settings
client_max_body_size 1000M;
root /var/www/mnemo_cards;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# ACME challenge for certificate renewal
location /.well-known/acme-challenge/ {
root /var/www/html;
try_files $uri =404;
}
}
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/javascript;
# Security - deny access to hidden files
location ~ /\. {
deny all;
}
}

View file

@ -0,0 +1,122 @@
# Nginx configuration for vscode.mnemo-cards.online
# Generated by generate-nginx-configs.sh on Sun Nov 23 02:51:54 MSK 2025
# Service: vscode
server {
listen 80;
server_name vscode.mnemo-cards.online;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name vscode.mnemo-cards.online;
# SSL configuration
ssl_certificate /etc/letsencrypt/live/mnemo-cards.online/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mnemo-cards.online/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# VSCode Server specific settings
client_max_body_size 100M;
# Rate limiting for VSCode
limit_req_zone $binary_remote_addr zone=vscode_general:10m rate=100r/m;
limit_req_zone $binary_remote_addr zone=vscode_login:10m rate=5r/m;
# Static files - no rate limiting
location ~ ^/(static|out|node_modules)/ {
proxy_pass http://127.0.0.1:8443;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_valid 200 1h;
add_header Cache-Control "public, max-age=3600";
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# Static file extensions - no rate limiting
location ~ \.(js|css|woff|woff2|ttf|eot|png|jpg|jpeg|gif|svg|ico|webp|map|json)$ {
proxy_pass http://127.0.0.1:8443;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_valid 200 1h;
add_header Cache-Control "public, max-age=3600";
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# Special rate limiting for login attempts
location /login {
limit_req zone=vscode_login burst=2 nodelay;
proxy_pass http://127.0.0.1:8443;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
proxy_buffering off;
proxy_request_buffering off;
}
# Main proxy location with rate limiting
location / {
limit_req zone=vscode_general burst=50 nodelay;
proxy_pass http://127.0.0.1:8443;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
proxy_buffering off;
proxy_request_buffering off;
}
}
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/javascript;
# Security - deny access to hidden files
location ~ /\. {
deny all;
}
}